1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 2016 by the Massachusetts Institute of Technology. 4 * All Rights Reserved. 5 * 6 * Export of this software from the United States of America may 7 * require a specific license from the United States Government. 8 * It is the responsibility of any person or organization contemplating 9 * export to obtain such a license before exporting. 10 * 11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 12 * distribute this software and its documentation for any purpose and 13 * without fee is hereby granted, provided that the above copyright 14 * notice appear in all copies and that both that copyright notice and 15 * this permission notice appear in supporting documentation, and that 16 * the name of M.I.T. not be used in advertising or publicity pertaining 17 * to distribution of the software without specific, written prior 18 * permission. Furthermore if you modify this software you must label 19 * your software as modified software and not distribute it in such a 20 * fashion that it might be confused with the original M.I.T. software. 21 * M.I.T. makes no representations about the suitability of 22 * this software for any purpose. It is provided "as is" without express 23 * or implied warranty. 24 */ 25 26 /* macOS requires this define for IPV6_PKTINFO. */ 27 #define __APPLE_USE_RFC_3542 28 29 #include "udppktinfo.h" 30 31 #include <netinet/in.h> 32 #include <sys/socket.h> 33 34 #if defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO) 35 #define HAVE_IP_PKTINFO 36 #endif 37 38 #if defined(IPV6_PKTINFO) && defined(HAVE_STRUCT_IN6_PKTINFO) 39 #define HAVE_IPV6_PKTINFO 40 #endif 41 42 #if defined(HAVE_IP_PKTINFO) || defined(IP_SENDSRCADDR) || \ 43 defined(HAVE_IPV6_PKTINFO) 44 #define HAVE_PKTINFO_SUPPORT 45 #endif 46 47 /* Use RFC 3542 API below, but fall back from IPV6_RECVPKTINFO to IPV6_PKTINFO 48 * for RFC 2292 implementations. */ 49 #if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO) 50 #define IPV6_RECVPKTINFO IPV6_PKTINFO 51 #endif 52 53 /* Parallel, though not standardized. */ 54 #if !defined(IP_RECVPKTINFO) && defined(IP_PKTINFO) 55 #define IP_RECVPKTINFO IP_PKTINFO 56 #endif /* IP_RECVPKTINFO */ 57 58 #if defined(CMSG_SPACE) && defined(HAVE_STRUCT_CMSGHDR) && \ 59 defined(HAVE_PKTINFO_SUPPORT) 60 union pktinfo { 61 #ifdef HAVE_STRUCT_IN6_PKTINFO 62 struct in6_pktinfo pi6; 63 #endif 64 #ifdef HAVE_STRUCT_IN_PKTINFO 65 struct in_pktinfo pi4; 66 #endif 67 #ifdef IP_RECVDSTADDR 68 struct in_addr iaddr; 69 #endif 70 char c; 71 }; 72 #endif /* HAVE_IPV6_PKTINFO && HAVE_STRUCT_CMSGHDR && HAVE_PKTINFO_SUPPORT */ 73 74 #ifdef HAVE_IP_PKTINFO 75 76 #define set_ipv4_pktinfo set_ipv4_recvpktinfo 77 static inline krb5_error_code 78 set_ipv4_recvpktinfo(int sock) 79 { 80 int sockopt = 1; 81 return setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO, &sockopt, 82 sizeof(sockopt)); 83 } 84 85 #elif defined(IP_RECVDSTADDR) /* HAVE_IP_PKTINFO */ 86 87 #define set_ipv4_pktinfo set_ipv4_recvdstaddr 88 static inline krb5_error_code 89 set_ipv4_recvdstaddr(int sock) 90 { 91 int sockopt = 1; 92 return setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &sockopt, 93 sizeof(sockopt)); 94 } 95 96 #else /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */ 97 #define set_ipv4_pktinfo(s) EINVAL 98 #endif /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */ 99 100 #ifdef HAVE_IPV6_PKTINFO 101 102 #define set_ipv6_pktinfo set_ipv6_recvpktinfo 103 static inline krb5_error_code 104 set_ipv6_recvpktinfo(int sock) 105 { 106 int sockopt = 1; 107 return setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &sockopt, 108 sizeof(sockopt)); 109 } 110 111 #else /* HAVE_IPV6_PKTINFO */ 112 #define set_ipv6_pktinfo(s) EINVAL 113 #endif /* HAVE_IPV6_PKTINFO */ 114 115 /* 116 * Set pktinfo option on a socket. Takes a socket and the socket address family 117 * as arguments. 118 * 119 * Returns 0 on success, EINVAL if pktinfo is not supported for the address 120 * family. 121 */ 122 krb5_error_code 123 set_pktinfo(int sock, int family) 124 { 125 switch (family) { 126 case AF_INET: 127 return set_ipv4_pktinfo(sock); 128 case AF_INET6: 129 return set_ipv6_pktinfo(sock); 130 default: 131 return EINVAL; 132 } 133 } 134 135 #if defined(HAVE_PKTINFO_SUPPORT) && defined(CMSG_SPACE) 136 137 /* 138 * Check if a socket is bound to a wildcard address. 139 * Returns 1 if it is, 0 if it's bound to a specific address, or -1 on error 140 * with errno set to the error. 141 */ 142 static int 143 is_socket_bound_to_wildcard(int sock) 144 { 145 struct sockaddr_storage bound_addr; 146 socklen_t bound_addr_len = sizeof(bound_addr); 147 struct sockaddr *sa = ss2sa(&bound_addr); 148 149 if (getsockname(sock, sa, &bound_addr_len) < 0) 150 return -1; 151 152 if (!sa_is_inet(sa)) { 153 errno = EINVAL; 154 return -1; 155 } 156 157 return sa_is_wildcard(sa); 158 } 159 160 #ifdef HAVE_IP_PKTINFO 161 162 static inline struct in_pktinfo * 163 cmsg2pktinfo(struct cmsghdr *cmsgptr) 164 { 165 return (struct in_pktinfo *)(void *)CMSG_DATA(cmsgptr); 166 } 167 168 #define check_cmsg_v4_pktinfo check_cmsg_ip_pktinfo 169 static int 170 check_cmsg_ip_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr_in *to, 171 aux_addressing_info *auxaddr) 172 { 173 struct in_pktinfo *pktinfo; 174 175 if (cmsgptr->cmsg_level == IPPROTO_IP && 176 cmsgptr->cmsg_type == IP_PKTINFO) { 177 memset(to, 0, sizeof(*to)); 178 pktinfo = cmsg2pktinfo(cmsgptr); 179 to->sin_addr = pktinfo->ipi_addr; 180 to->sin_family = AF_INET; 181 return 1; 182 } 183 return 0; 184 } 185 186 #elif defined(IP_RECVDSTADDR) /* HAVE_IP_PKTINFO */ 187 188 static inline struct in_addr * 189 cmsg2sin(struct cmsghdr *cmsgptr) 190 { 191 return (struct in_addr *)(void *)CMSG_DATA(cmsgptr); 192 } 193 194 #define check_cmsg_v4_pktinfo check_cmsg_ip_recvdstaddr 195 static int 196 check_cmsg_ip_recvdstaddr(struct cmsghdr *cmsgptr, struct sockaddr_in *to, 197 aux_addressing_info *auxaddr) 198 { 199 struct in_addr *sin_addr; 200 201 if (cmsgptr->cmsg_level == IPPROTO_IP && 202 cmsgptr->cmsg_type == IP_RECVDSTADDR) { 203 memset(to, 0, sizeof(*to)); 204 sin_addr = cmsg2sin(cmsgptr); 205 to->sin_addr = *sin_addr; 206 to->sin_family = AF_INET; 207 return 1; 208 } 209 return 0; 210 } 211 212 #else /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */ 213 #define check_cmsg_v4_pktinfo(c, t, l, a) 0 214 #endif /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */ 215 216 #ifdef HAVE_IPV6_PKTINFO 217 218 static inline struct in6_pktinfo * 219 cmsg2pktinfo6(struct cmsghdr *cmsgptr) 220 { 221 return (struct in6_pktinfo *)(void *)CMSG_DATA(cmsgptr); 222 } 223 224 #define check_cmsg_v6_pktinfo check_cmsg_ipv6_pktinfo 225 static int 226 check_cmsg_ipv6_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr_in6 *to, 227 aux_addressing_info *auxaddr) 228 { 229 struct in6_pktinfo *pktinfo; 230 231 if (cmsgptr->cmsg_level == IPPROTO_IPV6 && 232 cmsgptr->cmsg_type == IPV6_PKTINFO) { 233 memset(to, 0, sizeof(*to)); 234 pktinfo = cmsg2pktinfo6(cmsgptr); 235 to->sin6_addr = pktinfo->ipi6_addr; 236 to->sin6_family = AF_INET6; 237 auxaddr->ipv6_ifindex = pktinfo->ipi6_ifindex; 238 return 1; 239 } 240 return 0; 241 } 242 #else /* HAVE_IPV6_PKTINFO */ 243 #define check_cmsg_v6_pktinfo(c, t, l, a) 0 244 #endif /* HAVE_IPV6_PKTINFO */ 245 246 static int 247 check_cmsg_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr_storage *to, 248 aux_addressing_info *auxaddr) 249 { 250 return check_cmsg_v4_pktinfo(cmsgptr, ss2sin(to), auxaddr) || 251 check_cmsg_v6_pktinfo(cmsgptr, ss2sin6(to), auxaddr); 252 } 253 254 /* 255 * Receive a message from a socket. 256 * 257 * Arguments: 258 * sock 259 * buf - The buffer to store the message in. 260 * len - buf length 261 * flags 262 * from - Set to the address that sent the message 263 * to - Set to the address that the message was sent to if possible. 264 * May not be set in certain cases such as if pktinfo support is 265 * missing. May be NULL. 266 * auxaddr - Miscellaneous address information. 267 * 268 * Returns 0 on success, otherwise an error code. 269 */ 270 krb5_error_code 271 recv_from_to(int sock, void *buf, size_t len, int flags, 272 struct sockaddr_storage *from, struct sockaddr_storage *to, 273 aux_addressing_info *auxaddr) 274 275 { 276 int r; 277 struct iovec iov; 278 char cmsg[CMSG_SPACE(sizeof(union pktinfo))]; 279 struct cmsghdr *cmsgptr; 280 struct msghdr msg; 281 socklen_t fromlen = sizeof(*from); 282 283 /* Don't use pktinfo if the socket isn't bound to a wildcard address. */ 284 r = is_socket_bound_to_wildcard(sock); 285 if (r < 0) 286 return errno; 287 288 if (to == NULL || !r) 289 return recvfrom(sock, buf, len, flags, ss2sa(from), &fromlen); 290 291 /* Clobber with something recognizable in case we can't extract the address 292 * but try to use it anyways. */ 293 memset(to, 0x40, sizeof(*to)); 294 to->ss_family = AF_UNSPEC; 295 296 iov.iov_base = buf; 297 iov.iov_len = len; 298 memset(&msg, 0, sizeof(msg)); 299 msg.msg_name = ss2sa(from); 300 msg.msg_namelen = sizeof(*from); 301 msg.msg_iov = &iov; 302 msg.msg_iovlen = 1; 303 msg.msg_control = cmsg; 304 msg.msg_controllen = sizeof(cmsg); 305 306 r = recvmsg(sock, &msg, flags); 307 if (r < 0) 308 return r; 309 310 /* 311 * On Darwin (and presumably all *BSD with KAME stacks), CMSG_FIRSTHDR 312 * doesn't check for a non-zero controllen. RFC 3542 recommends making 313 * this check, even though the (new) spec for CMSG_FIRSTHDR says it's 314 * supposed to do the check. 315 */ 316 if (msg.msg_controllen) { 317 cmsgptr = CMSG_FIRSTHDR(&msg); 318 while (cmsgptr) { 319 if (check_cmsg_pktinfo(cmsgptr, to, auxaddr)) 320 return r; 321 cmsgptr = CMSG_NXTHDR(&msg, cmsgptr); 322 } 323 } 324 /* No info about destination addr was available. */ 325 return r; 326 } 327 328 #ifdef HAVE_IP_PKTINFO 329 330 #define set_msg_from_ipv4 set_msg_from_ip_pktinfo 331 static krb5_error_code 332 set_msg_from_ip_pktinfo(struct msghdr *msg, struct cmsghdr *cmsgptr, 333 const struct sockaddr_in *from, 334 aux_addressing_info *auxaddr) 335 { 336 struct in_pktinfo *p = cmsg2pktinfo(cmsgptr); 337 338 cmsgptr->cmsg_level = IPPROTO_IP; 339 cmsgptr->cmsg_type = IP_PKTINFO; 340 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); 341 p->ipi_spec_dst = from->sin_addr; 342 343 msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); 344 return 0; 345 } 346 347 #elif defined(IP_SENDSRCADDR) /* HAVE_IP_PKTINFO */ 348 349 #define set_msg_from_ipv4 set_msg_from_ip_sendsrcaddr 350 static krb5_error_code 351 set_msg_from_ip_sendsrcaddr(struct msghdr *msg, struct cmsghdr *cmsgptr, 352 const struct sockaddr_in *from, 353 aux_addressing_info *auxaddr) 354 { 355 struct in_addr *sin_addr = cmsg2sin(cmsgptr); 356 357 cmsgptr->cmsg_level = IPPROTO_IP; 358 cmsgptr->cmsg_type = IP_SENDSRCADDR; 359 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 360 msg->msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); 361 *sin_addr = from->sin_addr; 362 return 0; 363 } 364 365 #else /* HAVE_IP_PKTINFO || IP_SENDSRCADDR */ 366 #define set_msg_from_ipv4(m, c, f, l, a) EINVAL 367 #endif /* HAVE_IP_PKTINFO || IP_SENDSRCADDR */ 368 369 #ifdef HAVE_IPV6_PKTINFO 370 371 #define set_msg_from_ipv6 set_msg_from_ipv6_pktinfo 372 static krb5_error_code 373 set_msg_from_ipv6_pktinfo(struct msghdr *msg, struct cmsghdr *cmsgptr, 374 const struct sockaddr_in6 *from, 375 aux_addressing_info *auxaddr) 376 { 377 struct in6_pktinfo *p = cmsg2pktinfo6(cmsgptr); 378 379 cmsgptr->cmsg_level = IPPROTO_IPV6; 380 cmsgptr->cmsg_type = IPV6_PKTINFO; 381 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 382 383 p->ipi6_addr = from->sin6_addr; 384 /* 385 * Because of the possibility of asymmetric routing, we 386 * normally don't want to specify an interface. However, 387 * macOS doesn't like sending from a link-local address 388 * (which can come up in testing at least, if you wind up 389 * with a "foo.local" name) unless we do specify the 390 * interface. 391 */ 392 if (IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) 393 p->ipi6_ifindex = auxaddr->ipv6_ifindex; 394 /* otherwise, already zero */ 395 396 msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); 397 return 0; 398 } 399 400 #else /* HAVE_IPV6_PKTINFO */ 401 #define set_msg_from_ipv6(m, c, f, l, a) EINVAL 402 #endif /* HAVE_IPV6_PKTINFO */ 403 404 static krb5_error_code 405 set_msg_from(struct msghdr *msg, struct cmsghdr *cmsgptr, 406 const struct sockaddr *from, aux_addressing_info *auxaddr) 407 { 408 switch (from->sa_family) { 409 case AF_INET: 410 return set_msg_from_ipv4(msg, cmsgptr, sa2sin(from), auxaddr); 411 case AF_INET6: 412 return set_msg_from_ipv6(msg, cmsgptr, sa2sin6(from), auxaddr); 413 } 414 415 return EINVAL; 416 } 417 418 /* 419 * Send a message to an address. 420 * 421 * Arguments: 422 * sock 423 * buf - The message to send. 424 * len - buf length 425 * flags 426 * to - The address to send the message to. 427 * from - The address to attempt to send the message from. May be NULL. 428 * auxaddr - Miscellaneous address information. 429 * 430 * Returns 0 on success, otherwise an error code. 431 */ 432 krb5_error_code 433 send_to_from(int sock, void *buf, size_t len, int flags, 434 const struct sockaddr *to, const struct sockaddr *from, 435 aux_addressing_info *auxaddr) 436 { 437 int r; 438 struct iovec iov; 439 struct msghdr msg; 440 struct cmsghdr *cmsgptr; 441 char cbuf[CMSG_SPACE(sizeof(union pktinfo))]; 442 443 /* Don't use pktinfo if the socket isn't bound to a wildcard address. */ 444 r = is_socket_bound_to_wildcard(sock); 445 if (r < 0) 446 return errno; 447 448 if (from == NULL || from->sa_family != to->sa_family || !r) 449 goto use_sendto; 450 451 iov.iov_base = buf; 452 iov.iov_len = len; 453 /* Truncation? */ 454 if (iov.iov_len != len) 455 return EINVAL; 456 memset(cbuf, 0, sizeof(cbuf)); 457 memset(&msg, 0, sizeof(msg)); 458 msg.msg_name = (void *)to; 459 msg.msg_namelen = sa_socklen(to); 460 msg.msg_iov = &iov; 461 msg.msg_iovlen = 1; 462 msg.msg_control = cbuf; 463 /* CMSG_FIRSTHDR needs a non-zero controllen, or it'll return NULL on 464 * Linux. */ 465 msg.msg_controllen = sizeof(cbuf); 466 cmsgptr = CMSG_FIRSTHDR(&msg); 467 msg.msg_controllen = 0; 468 469 if (set_msg_from(&msg, cmsgptr, from, auxaddr)) 470 goto use_sendto; 471 return sendmsg(sock, &msg, flags); 472 473 use_sendto: 474 return sendto(sock, buf, len, flags, to, sa_socklen(to)); 475 } 476 477 #else /* HAVE_PKTINFO_SUPPORT && CMSG_SPACE */ 478 479 krb5_error_code 480 recv_from_to(int sock, void *buf, size_t len, int flags, 481 struct sockaddr_storage *from, struct sockaddr_storage *to, 482 aux_addressing_info *auxaddr) 483 { 484 socklen_t fromlen = sizeof(*from); 485 486 if (to != NULL) { 487 /* Clobber with something recognizable in case we try to use the 488 * address. */ 489 memset(to, 0x40, sizeof(*to)); 490 to->ss_family = AF_UNSPEC; 491 } 492 493 return recvfrom(sock, buf, len, flags, ss2sa(from), &fromlen); 494 } 495 496 krb5_error_code 497 send_to_from(int sock, void *buf, size_t len, int flags, 498 const struct sockaddr *to, const struct sockaddr *from, 499 aux_addressing_info *auxaddr) 500 { 501 return sendto(sock, buf, len, flags, to, sa_socklen(to)); 502 } 503 504 #endif /* HAVE_PKTINFO_SUPPORT && CMSG_SPACE */ 505