1 /* 2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /* $Id: sockaddr.c,v 1.15 2020/12/21 11:41:09 florian Exp $ */ 18 19 /*! \file */ 20 #include <sys/types.h> 21 #include <sys/socket.h> 22 #include <netdb.h> 23 #include <stdio.h> 24 25 #include <isc/buffer.h> 26 27 #include <isc/region.h> 28 #include <isc/sockaddr.h> 29 #include <string.h> 30 #include <isc/util.h> 31 32 int 33 isc_sockaddr_equal(const struct sockaddr_storage *a, const struct sockaddr_storage *b) { 34 return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR| 35 ISC_SOCKADDR_CMPPORT| 36 ISC_SOCKADDR_CMPSCOPE)); 37 } 38 39 int 40 isc_sockaddr_eqaddr(const struct sockaddr_storage *a, const struct sockaddr_storage *b) { 41 return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR| 42 ISC_SOCKADDR_CMPSCOPE)); 43 } 44 45 int 46 isc_sockaddr_compare(const struct sockaddr_storage *a, const struct sockaddr_storage *b, 47 unsigned int flags) 48 { 49 struct sockaddr_in *sin_a, *sin_b; 50 struct sockaddr_in6 *sin6_a, *sin6_b; 51 52 REQUIRE(a != NULL && b != NULL); 53 54 if (a->ss_len != b->ss_len) 55 return (0); 56 57 /* 58 * We don't just memcmp because the sin_zero field isn't always 59 * zero. 60 */ 61 62 if (a->ss_family != b->ss_family) 63 return (0); 64 switch (a->ss_family) { 65 case AF_INET: 66 sin_a = (struct sockaddr_in *) a; 67 sin_b = (struct sockaddr_in *) b; 68 if ((flags & ISC_SOCKADDR_CMPADDR) != 0 && 69 memcmp(&sin_a->sin_addr, &sin_b->sin_addr, 70 sizeof(sin_a->sin_addr)) != 0) 71 return (0); 72 if ((flags & ISC_SOCKADDR_CMPPORT) != 0 && 73 sin_a->sin_port != sin_b->sin_port) 74 return (0); 75 break; 76 case AF_INET6: 77 sin6_a = (struct sockaddr_in6 *) a; 78 sin6_b = (struct sockaddr_in6 *) b; 79 80 if ((flags & ISC_SOCKADDR_CMPADDR) != 0 && 81 memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr, 82 sizeof(sin6_a->sin6_addr)) != 0) 83 return (0); 84 /* 85 * If ISC_SOCKADDR_CMPSCOPEZERO is set then don't return 86 * 0 if one of the scopes in zero. 87 */ 88 if ((flags & ISC_SOCKADDR_CMPSCOPE) != 0 && 89 sin6_a->sin6_scope_id != sin6_b->sin6_scope_id && 90 ((flags & ISC_SOCKADDR_CMPSCOPEZERO) == 0 || 91 (sin6_a->sin6_scope_id != 0 && 92 sin6_b->sin6_scope_id != 0))) 93 return (0); 94 if ((flags & ISC_SOCKADDR_CMPPORT) != 0 && 95 sin6_a->sin6_port != sin6_b->sin6_port) 96 return (0); 97 break; 98 default: 99 if (memcmp(a, b, a->ss_len) != 0) 100 return (0); 101 } 102 return (1); 103 } 104 105 isc_result_t 106 isc_sockaddr_totext(const struct sockaddr_storage *sockaddr, isc_buffer_t *target) { 107 struct sockaddr_in *sin; 108 struct sockaddr_in6 *sin6; 109 char pbuf[sizeof("65000")]; 110 unsigned int plen; 111 isc_region_t avail; 112 int error; 113 char tmp[NI_MAXHOST]; 114 115 REQUIRE(sockaddr != NULL); 116 117 /* 118 * Do the port first, giving us the opportunity to check for 119 * unsupported address families. 120 */ 121 switch (sockaddr->ss_family) { 122 case AF_INET: 123 sin = (struct sockaddr_in *)sockaddr; 124 snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sin->sin_port)); 125 break; 126 case AF_INET6: 127 sin6 = (struct sockaddr_in6 *)sockaddr; 128 snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sin6->sin6_port)); 129 break; 130 default: 131 return (ISC_R_FAILURE); 132 } 133 134 plen = strlen(pbuf); 135 INSIST(plen < sizeof(pbuf)); 136 137 error = getnameinfo((struct sockaddr *)sockaddr, sockaddr->ss_len, 138 tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); 139 if (strlen(tmp) > isc_buffer_availablelength(target)) 140 return (ISC_R_NOSPACE); 141 isc_buffer_putmem(target, tmp, strlen(tmp)); 142 143 if (1 + plen + 1 > isc_buffer_availablelength(target)) 144 return (ISC_R_NOSPACE); 145 146 isc_buffer_putmem(target, (const unsigned char *)"#", 1); 147 isc_buffer_putmem(target, (const unsigned char *)pbuf, plen); 148 149 /* 150 * Null terminate after used region. 151 */ 152 isc_buffer_availableregion(target, &avail); 153 INSIST(avail.length >= 1); 154 avail.base[0] = '\0'; 155 156 return (ISC_R_SUCCESS); 157 } 158 159 void 160 isc_sockaddr_format(const struct sockaddr_storage *sa, char *array, unsigned int size) { 161 isc_result_t result; 162 isc_buffer_t buf; 163 164 if (size == 0U) 165 return; 166 167 isc_buffer_init(&buf, array, size); 168 result = isc_sockaddr_totext(sa, &buf); 169 if (result != ISC_R_SUCCESS) { 170 snprintf(array, size, "<unknown address, family %u>", 171 sa->ss_family); 172 array[size - 1] = '\0'; 173 } 174 } 175 176 void 177 isc_sockaddr_any(struct sockaddr_storage *sockaddr) 178 { 179 struct sockaddr_in *sin = (struct sockaddr_in *) sockaddr; 180 memset(sockaddr, 0, sizeof(*sockaddr)); 181 sin->sin_family = AF_INET; 182 sin->sin_len = sizeof(*sin); 183 sin->sin_addr.s_addr = INADDR_ANY; 184 sin->sin_port = 0; 185 } 186 187 void 188 isc_sockaddr_any6(struct sockaddr_storage *sockaddr) 189 { 190 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sockaddr; 191 memset(sockaddr, 0, sizeof(*sockaddr)); 192 sin6->sin6_family = AF_INET6; 193 sin6->sin6_len = sizeof(*sin6); 194 sin6->sin6_addr = in6addr_any; 195 sin6->sin6_port = 0; 196 } 197 198 void 199 isc_sockaddr_anyofpf(struct sockaddr_storage *sockaddr, int pf) { 200 switch (pf) { 201 case AF_INET: 202 isc_sockaddr_any(sockaddr); 203 break; 204 case AF_INET6: 205 isc_sockaddr_any6(sockaddr); 206 break; 207 default: 208 INSIST(0); 209 } 210 } 211 212 int 213 isc_sockaddr_pf(const struct sockaddr_storage *sockaddr) { 214 215 /* 216 * Get the protocol family of 'sockaddr'. 217 */ 218 219 return (sockaddr->ss_family); 220 } 221 222 in_port_t 223 isc_sockaddr_getport(const struct sockaddr_storage *sockaddr) { 224 struct sockaddr_in *sin; 225 struct sockaddr_in6 *sin6; 226 227 switch (sockaddr->ss_family) { 228 case AF_INET: 229 sin = (struct sockaddr_in *)sockaddr; 230 return (ntohs(sin->sin_port)); 231 break; 232 case AF_INET6: 233 sin6 = (struct sockaddr_in6 *)sockaddr; 234 return (ntohs(sin6->sin6_port)); 235 break; 236 default: 237 FATAL_ERROR(__FILE__, __LINE__, 238 "unknown address family: %d", 239 (int)sockaddr->ss_family); 240 } 241 } 242 243 int 244 isc_sockaddr_ismulticast(const struct sockaddr_storage *sockaddr) { 245 struct sockaddr_in *sin; 246 struct sockaddr_in6 *sin6; 247 248 switch (sockaddr->ss_family) { 249 case AF_INET: 250 sin = (struct sockaddr_in *)sockaddr; 251 return (IN_MULTICAST(sin->sin_addr.s_addr)); 252 case AF_INET6: 253 sin6 = (struct sockaddr_in6 *)sockaddr; 254 return (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)); 255 default: 256 return (0); 257 } 258 } 259 260 int 261 isc_sockaddr_issitelocal(const struct sockaddr_storage *sockaddr) { 262 struct sockaddr_in6 *sin6; 263 if (sockaddr->ss_family == AF_INET6) { 264 sin6 = (struct sockaddr_in6 *)sockaddr; 265 return (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)); 266 } 267 return (0); 268 } 269 270 int 271 isc_sockaddr_islinklocal(const struct sockaddr_storage *sockaddr) { 272 struct sockaddr_in6 *sin6; 273 if (sockaddr->ss_family == AF_INET6) { 274 sin6 = (struct sockaddr_in6 *)sockaddr; 275 return (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)); 276 } 277 return (0); 278 } 279