sockaddr.c 12.2 KB
Newer Older
Michael Graff's avatar
Michael Graff committed
1
/*
Mark Andrews's avatar
Mark Andrews committed
2 3
 * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
 * Copyright (C) 1999-2003  Internet Software Consortium.
4
 *
Michael Graff's avatar
Michael Graff committed
5 6 7
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
Michael Graff's avatar
Michael Graff committed
16 17
 */

18
/* $Id: sockaddr.c,v 1.61 2004/11/22 23:29:10 marka Exp $ */
David Lawrence's avatar
David Lawrence committed
19

Michael Graff's avatar
Michael Graff committed
20 21
#include <config.h>

Mark Andrews's avatar
Mark Andrews committed
22
#include <stdio.h>
Michael Graff's avatar
Michael Graff committed
23

24
#include <isc/buffer.h>
25
#include <isc/hash.h>
26
#include <isc/msgs.h>
27
#include <isc/netaddr.h>
28
#include <isc/print.h>
29 30
#include <isc/region.h>
#include <isc/sockaddr.h>
31
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
32
#include <isc/util.h>
Michael Graff's avatar
Michael Graff committed
33 34

isc_boolean_t
35
isc_sockaddr_equal(const isc_sockaddr_t *a, const isc_sockaddr_t *b) {
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
	return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR|
					   ISC_SOCKADDR_CMPPORT|
					   ISC_SOCKADDR_CMPSCOPE));
}

isc_boolean_t
isc_sockaddr_eqaddr(const isc_sockaddr_t *a, const isc_sockaddr_t *b) {
	return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR|
					   ISC_SOCKADDR_CMPSCOPE));
}

isc_boolean_t
isc_sockaddr_compare(const isc_sockaddr_t *a, const isc_sockaddr_t *b,
		     unsigned int flags)
{
Bob Halley's avatar
Bob Halley committed
51
	REQUIRE(a != NULL && b != NULL);
Michael Graff's avatar
Michael Graff committed
52

Bob Halley's avatar
Bob Halley committed
53
	if (a->length != b->length)
Michael Graff's avatar
Michael Graff committed
54 55
		return (ISC_FALSE);

56 57 58 59 60 61
	/*
	 * We don't just memcmp because the sin_zero field isn't always
	 * zero.
	 */

	if (a->type.sa.sa_family != b->type.sa.sa_family)
Michael Graff's avatar
Michael Graff committed
62
		return (ISC_FALSE);
63 64
	switch (a->type.sa.sa_family) {
	case AF_INET:
65 66
		if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
		    memcmp(&a->type.sin.sin_addr, &b->type.sin.sin_addr,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
67
			   sizeof(a->type.sin.sin_addr)) != 0)
68
			return (ISC_FALSE);
69 70
		if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
		    a->type.sin.sin_port != b->type.sin.sin_port)
71 72 73
			return (ISC_FALSE);
		break;
	case AF_INET6:
74 75
		if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
		    memcmp(&a->type.sin6.sin6_addr, &b->type.sin6.sin6_addr,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
76
			   sizeof(a->type.sin6.sin6_addr)) != 0)
77
			return (ISC_FALSE);
78
#ifdef ISC_PLATFORM_HAVESCOPEID
79 80 81 82 83 84 85 86 87
		/*
		 * If ISC_SOCKADDR_CMPSCOPEZERO is set then don't return
		 * ISC_FALSE if one of the scopes in zero.
		 */
		if ((flags & ISC_SOCKADDR_CMPSCOPE) != 0 &&
		    a->type.sin6.sin6_scope_id != b->type.sin6.sin6_scope_id &&
		    ((flags & ISC_SOCKADDR_CMPSCOPEZERO) == 0 ||
		      (a->type.sin6.sin6_scope_id != 0 &&
		       b->type.sin6.sin6_scope_id != 0)))
88 89
			return (ISC_FALSE);
#endif
90 91
		if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
		    a->type.sin6.sin6_port != b->type.sin6.sin6_port)
92 93 94 95 96 97 98 99 100
			return (ISC_FALSE);
		break;
	default:
		if (memcmp(&a->type, &b->type, a->length) != 0)
			return (ISC_FALSE);
	}
	return (ISC_TRUE);
}

101 102 103 104
isc_boolean_t
isc_sockaddr_eqaddrprefix(const isc_sockaddr_t *a, const isc_sockaddr_t *b,
			  unsigned int prefixlen)
{
105 106 107 108
	isc_netaddr_t na, nb;
	isc_netaddr_fromsockaddr(&na, a);
	isc_netaddr_fromsockaddr(&nb, b);
	return (isc_netaddr_eqprefix(&na, &nb, prefixlen));
109 110
}

111 112
isc_result_t
isc_sockaddr_totext(const isc_sockaddr_t *sockaddr, isc_buffer_t *target) {
113 114
	isc_result_t result;
	isc_netaddr_t netaddr;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
115
	char pbuf[sizeof("65000")];
116 117
	unsigned int plen;
	isc_region_t avail;
Mark Andrews's avatar
Mark Andrews committed
118 119 120

	REQUIRE(sockaddr != NULL);

121 122
	/*
	 * Do the port first, giving us the opportunity to check for
123
	 * unsupported address families before calling
124 125
	 * isc_netaddr_fromsockaddr().
	 */
126
	switch (sockaddr->type.sa.sa_family) {
Mark Andrews's avatar
Mark Andrews committed
127
	case AF_INET:
128
		snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sockaddr->type.sin.sin_port));
Mark Andrews's avatar
Mark Andrews committed
129 130
		break;
	case AF_INET6:
131
		snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sockaddr->type.sin6.sin6_port));
Mark Andrews's avatar
Mark Andrews committed
132 133
		break;
	default:
134
		return (ISC_R_FAILURE);
Mark Andrews's avatar
Mark Andrews committed
135
	}
136

137
	plen = strlen(pbuf);
138
	INSIST(plen < sizeof(pbuf));
139

140 141 142 143
	isc_netaddr_fromsockaddr(&netaddr, sockaddr);
	result = isc_netaddr_totext(&netaddr, target);
	if (result != ISC_R_SUCCESS)
		return (result);
144

145
	if (1 + plen + 1 > isc_buffer_availablelength(target))
146
		return (ISC_R_NOSPACE);
147

David Lawrence's avatar
David Lawrence committed
148 149
	isc_buffer_putmem(target, (const unsigned char *)"#", 1);
	isc_buffer_putmem(target, (const unsigned char *)pbuf, plen);
150

David Lawrence's avatar
David Lawrence committed
151 152 153
	/*
	 * Null terminate after used region.
	 */
154
	isc_buffer_availableregion(target, &avail);
155 156 157
	INSIST(avail.length >= 1);
	avail.base[0] = '\0';

158
	return (ISC_R_SUCCESS);
Mark Andrews's avatar
Mark Andrews committed
159 160
}

161
void
Andreas Gustafsson's avatar
Andreas Gustafsson committed
162
isc_sockaddr_format(const isc_sockaddr_t *sa, char *array, unsigned int size) {
163 164 165 166 167 168
	isc_result_t result;
	isc_buffer_t buf;

	isc_buffer_init(&buf, array, size);
	result = isc_sockaddr_totext(sa, &buf);
	if (result != ISC_R_SUCCESS) {
169 170 171
		/*
		 * The message is the same as in netaddr.c.
		 */
172
		snprintf(array, size,
173 174 175
			 isc_msgcat_get(isc_msgcat, ISC_MSGSET_NETADDR,
					ISC_MSG_UNKNOWNADDR,
					"<unknown address, family %u>"),
176 177 178 179 180
			 sa->type.sa.sa_family);
		array[size - 1] = '\0';
	}
}

Bob Halley's avatar
Bob Halley committed
181
unsigned int
182
isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, isc_boolean_t address_only) {
183 184
	unsigned int length = 0;
	const unsigned char *s = NULL;
Bob Halley's avatar
Bob Halley committed
185 186
	unsigned int h = 0;
	unsigned int g;
187
	unsigned int p = 0;
188
	const struct in6_addr *in6;
189

Bob Halley's avatar
Bob Halley committed
190 191
	REQUIRE(sockaddr != NULL);

192 193
	switch (sockaddr->type.sa.sa_family) {
	case AF_INET:
194
		s = (const unsigned char *)&sockaddr->type.sin.sin_addr;
195
		p = ntohs(sockaddr->type.sin.sin_port);
196
		length = sizeof(sockaddr->type.sin.sin_addr.s_addr);
197 198 199 200
		break;
	case AF_INET6:
		in6 = &sockaddr->type.sin6.sin6_addr;
		if (IN6_IS_ADDR_V4MAPPED(in6)) {
201 202
			s = (const unsigned char *)&in6[12];
			length = sizeof(sockaddr->type.sin.sin_addr.s_addr);
203
		} else {
204
			s = (const unsigned char *)in6;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
205
			length = sizeof(sockaddr->type.sin6.sin6_addr);
Bob Halley's avatar
Bob Halley committed
206
		}
207 208 209 210 211 212 213 214 215
		p = ntohs(sockaddr->type.sin6.sin6_port);
		break;
	default:
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 isc_msgcat_get(isc_msgcat,
						ISC_MSGSET_SOCKADDR,
						ISC_MSG_UNKNOWNFAMILY,
						"unknown address family: %d"),
					     (int)sockaddr->type.sa.sa_family);
David Lawrence's avatar
David Lawrence committed
216
		s = (const unsigned char *)&sockaddr->type;
Bob Halley's avatar
Bob Halley committed
217
		length = sockaddr->length;
218
		p = 0;
Michael Graff's avatar
Michael Graff committed
219
	}
220 221

	h = isc_hash_calc(s, length, ISC_TRUE);
222
	if (!address_only) {
223 224 225
		g = isc_hash_calc((const unsigned char *)&p, sizeof(p),
				  ISC_TRUE);
		h = h ^ g; /* XXX: we should concatenate h and p first */
226
	}
227

Bob Halley's avatar
Bob Halley committed
228
	return (h);
Michael Graff's avatar
Michael Graff committed
229
}
Bob Halley's avatar
Bob Halley committed
230

231 232 233
void
isc_sockaddr_any(isc_sockaddr_t *sockaddr)
{
Andreas Gustafsson's avatar
Andreas Gustafsson committed
234
	memset(sockaddr, 0, sizeof(*sockaddr));
235 236
	sockaddr->type.sin.sin_family = AF_INET;
#ifdef ISC_PLATFORM_HAVESALEN
Andreas Gustafsson's avatar
Andreas Gustafsson committed
237
	sockaddr->type.sin.sin_len = sizeof(sockaddr->type.sin);
238 239 240
#endif
	sockaddr->type.sin.sin_addr.s_addr = INADDR_ANY;
	sockaddr->type.sin.sin_port = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
241
	sockaddr->length = sizeof(sockaddr->type.sin);
242 243 244 245 246 247
	ISC_LINK_INIT(sockaddr, link);
}

void
isc_sockaddr_any6(isc_sockaddr_t *sockaddr)
{
Andreas Gustafsson's avatar
Andreas Gustafsson committed
248
	memset(sockaddr, 0, sizeof(*sockaddr));
249 250
	sockaddr->type.sin6.sin6_family = AF_INET6;
#ifdef ISC_PLATFORM_HAVESALEN
Andreas Gustafsson's avatar
Andreas Gustafsson committed
251
	sockaddr->type.sin6.sin6_len = sizeof(sockaddr->type.sin6);
252 253 254
#endif
	sockaddr->type.sin6.sin6_addr = in6addr_any;
	sockaddr->type.sin6.sin6_port = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
255
	sockaddr->length = sizeof(sockaddr->type.sin6);
256 257 258
	ISC_LINK_INIT(sockaddr, link);
}

Bob Halley's avatar
Bob Halley committed
259
void
260
isc_sockaddr_fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina,
261
		    in_port_t port)
Bob Halley's avatar
Bob Halley committed
262
{
Andreas Gustafsson's avatar
Andreas Gustafsson committed
263
	memset(sockaddr, 0, sizeof(*sockaddr));
Bob Halley's avatar
Bob Halley committed
264
	sockaddr->type.sin.sin_family = AF_INET;
265
#ifdef ISC_PLATFORM_HAVESALEN
Andreas Gustafsson's avatar
Andreas Gustafsson committed
266
	sockaddr->type.sin.sin_len = sizeof(sockaddr->type.sin);
Bob Halley's avatar
Bob Halley committed
267 268 269
#endif
	sockaddr->type.sin.sin_addr = *ina;
	sockaddr->type.sin.sin_port = htons(port);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
270
	sockaddr->length = sizeof(sockaddr->type.sin);
Bob Halley's avatar
Bob Halley committed
271 272 273
	ISC_LINK_INIT(sockaddr, link);
}

274 275 276 277 278 279 280 281 282 283 284 285 286 287
void
isc_sockaddr_anyofpf(isc_sockaddr_t *sockaddr, int pf) {
     switch (pf) {
     case AF_INET:
	     isc_sockaddr_any(sockaddr);
	     break;
     case AF_INET6:
	     isc_sockaddr_any6(sockaddr);
	     break;
     default:
	     INSIST(0);
     }
}

Bob Halley's avatar
Bob Halley committed
288
void
289
isc_sockaddr_fromin6(isc_sockaddr_t *sockaddr, const struct in6_addr *ina6,
290
		     in_port_t port)
Bob Halley's avatar
Bob Halley committed
291
{
Andreas Gustafsson's avatar
Andreas Gustafsson committed
292
	memset(sockaddr, 0, sizeof(*sockaddr));
Bob Halley's avatar
Bob Halley committed
293
	sockaddr->type.sin6.sin6_family = AF_INET6;
294
#ifdef ISC_PLATFORM_HAVESALEN
Andreas Gustafsson's avatar
Andreas Gustafsson committed
295
	sockaddr->type.sin6.sin6_len = sizeof(sockaddr->type.sin6);
Bob Halley's avatar
Bob Halley committed
296 297 298
#endif
	sockaddr->type.sin6.sin6_addr = *ina6;
	sockaddr->type.sin6.sin6_port = htons(port);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
299
	sockaddr->length = sizeof(sockaddr->type.sin6);
Bob Halley's avatar
Bob Halley committed
300 301
	ISC_LINK_INIT(sockaddr, link);
}
302 303

void
304
isc_sockaddr_v6fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina,
305
		      in_port_t port)
306
{
Andreas Gustafsson's avatar
Andreas Gustafsson committed
307
	memset(sockaddr, 0, sizeof(*sockaddr));
308
	sockaddr->type.sin6.sin6_family = AF_INET6;
309
#ifdef ISC_PLATFORM_HAVESALEN
Andreas Gustafsson's avatar
Andreas Gustafsson committed
310
	sockaddr->type.sin6.sin6_len = sizeof(sockaddr->type.sin6);
311
#endif
312 313
	sockaddr->type.sin6.sin6_addr.s6_addr[10] = 0xff;
	sockaddr->type.sin6.sin6_addr.s6_addr[11] = 0xff;
314 315
	memcpy(&sockaddr->type.sin6.sin6_addr.s6_addr[12], ina, 4);
	sockaddr->type.sin6.sin6_port = htons(port);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
316
	sockaddr->length = sizeof(sockaddr->type.sin6);
317 318 319 320
	ISC_LINK_INIT(sockaddr, link);
}

int
321
isc_sockaddr_pf(const isc_sockaddr_t *sockaddr) {
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336

	/*
	 * Get the protocol family of 'sockaddr'.
	 */

#if (AF_INET == PF_INET && AF_INET6 == PF_INET6)
	/*
	 * Assume that PF_xxx == AF_xxx for all AF and PF.
	 */
	return (sockaddr->type.sa.sa_family);
#else
	switch (sockaddr->type.sa.sa_family) {
	case AF_INET:
		return (PF_INET);
	case AF_INET6:
337
		return (PF_INET6);
338
	default:
339 340 341 342 343
		FATAL_ERROR(__FILE__, __LINE__,
			    isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKADDR,
					   ISC_MSG_UNKNOWNFAMILY,
					   "unknown address family: %d"),
			    (int)sockaddr->type.sa.sa_family);
344 345 346
	}
#endif
}
Bob Halley's avatar
Bob Halley committed
347

348 349 350 351
void
isc_sockaddr_fromnetaddr(isc_sockaddr_t *sockaddr, const isc_netaddr_t *na,
		    in_port_t port)
{
Andreas Gustafsson's avatar
Andreas Gustafsson committed
352
	memset(sockaddr, 0, sizeof(*sockaddr));
353 354 355
	sockaddr->type.sin.sin_family = na->family;
	switch (na->family) {
	case AF_INET:
Andreas Gustafsson's avatar
Andreas Gustafsson committed
356
		sockaddr->length = sizeof(sockaddr->type.sin);
357
#ifdef ISC_PLATFORM_HAVESALEN
Andreas Gustafsson's avatar
Andreas Gustafsson committed
358
		sockaddr->type.sin.sin_len = sizeof(sockaddr->type.sin);
359 360 361 362 363
#endif
		sockaddr->type.sin.sin_addr = na->type.in;
		sockaddr->type.sin.sin_port = htons(port);
		break;
	case AF_INET6:
Andreas Gustafsson's avatar
Andreas Gustafsson committed
364
		sockaddr->length = sizeof(sockaddr->type.sin6);
365
#ifdef ISC_PLATFORM_HAVESALEN
Andreas Gustafsson's avatar
Andreas Gustafsson committed
366
		sockaddr->type.sin6.sin6_len = sizeof(sockaddr->type.sin6);
367 368
#endif
		memcpy(&sockaddr->type.sin6.sin6_addr, &na->type.in6, 16);
369 370 371
#ifdef ISC_PLATFORM_HAVESCOPEID
		sockaddr->type.sin6.sin6_scope_id = isc_netaddr_getzone(na);
#endif
372 373 374 375 376 377 378 379
		sockaddr->type.sin6.sin6_port = htons(port);
		break;
        default:
                INSIST(0);
	}
	ISC_LINK_INIT(sockaddr, link);
}

Bob Halley's avatar
Bob Halley committed
380 381 382 383 384 385 386 387 388 389
void
isc_sockaddr_setport(isc_sockaddr_t *sockaddr, in_port_t port) {
	switch (sockaddr->type.sa.sa_family) {
	case AF_INET:
		sockaddr->type.sin.sin_port = htons(port);
		break;
	case AF_INET6:
		sockaddr->type.sin6.sin6_port = htons(port);
		break;
	default:
390 391 392 393 394
		FATAL_ERROR(__FILE__, __LINE__,
			    isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKADDR,
					   ISC_MSG_UNKNOWNFAMILY,
					   "unknown address family: %d"),
			    (int)sockaddr->type.sa.sa_family);
Bob Halley's avatar
Bob Halley committed
395 396
	}
}
David Lawrence's avatar
David Lawrence committed
397 398 399 400 401 402 403 404 405 406 407 408 409

in_port_t
isc_sockaddr_getport(isc_sockaddr_t *sockaddr) {
	in_port_t port = 0;

	switch (sockaddr->type.sa.sa_family) {
	case AF_INET:
		port = ntohs(sockaddr->type.sin.sin_port);
		break;
	case AF_INET6:
		port = ntohs(sockaddr->type.sin6.sin6_port);
		break;
	default:
410 411 412 413 414
		FATAL_ERROR(__FILE__, __LINE__,
			    isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKADDR,
					   ISC_MSG_UNKNOWNFAMILY,
					   "unknown address family: %d"),
			    (int)sockaddr->type.sa.sa_family);
David Lawrence's avatar
David Lawrence committed
415 416 417 418
	}

	return (port);
}
419 420 421 422 423 424 425 426

isc_boolean_t
isc_sockaddr_ismulticast(isc_sockaddr_t *sockaddr) {
	isc_netaddr_t netaddr;

	isc_netaddr_fromsockaddr(&netaddr, sockaddr);
	return (isc_netaddr_ismulticast(&netaddr));
}
427

428 429 430 431 432 433 434 435 436 437 438
isc_boolean_t
isc_sockaddr_isexperimental(isc_sockaddr_t *sockaddr) {
	isc_netaddr_t netaddr;

	if (sockaddr->type.sa.sa_family == AF_INET) {
		isc_netaddr_fromsockaddr(&netaddr, sockaddr);
		return (isc_netaddr_isexperimental(&netaddr));
	}
	return (ISC_FALSE);
}

439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
isc_boolean_t
isc_sockaddr_issitelocal(isc_sockaddr_t *sockaddr) {
	isc_netaddr_t netaddr;

	if (sockaddr->type.sa.sa_family == AF_INET6) {
		isc_netaddr_fromsockaddr(&netaddr, sockaddr);
		return (isc_netaddr_issitelocal(&netaddr));
	}
	return (ISC_FALSE);
}

isc_boolean_t
isc_sockaddr_islinklocal(isc_sockaddr_t *sockaddr) {
	isc_netaddr_t netaddr;

	if (sockaddr->type.sa.sa_family == AF_INET6) {
		isc_netaddr_fromsockaddr(&netaddr, sockaddr);
		return (isc_netaddr_islinklocal(&netaddr));
	}
	return (ISC_FALSE);
}