1 /* adapted from ipsec-tools 0.6 src/racoon/sockmisc.c */ 2 /* 3 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the project nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 #include <sys/types.h> 31 #include <sys/socket.h> 32 #include <sys/uio.h> 33 #include <netinet/in.h> 34 #include <string.h> 35 36 #include "recvfromto.h" 37 38 /* 39 * Receive packet, with src/dst information. It is assumed that necessary 40 * setsockopt() have already performed on socket. 41 */ 42 int 43 recvfromto(s, buf, buflen, flags, from, fromlen, to, tolen) 44 int s; 45 void *buf; 46 size_t buflen; 47 int flags; 48 struct sockaddr *from; 49 u_int *fromlen; 50 struct sockaddr *to; 51 u_int *tolen; 52 { 53 int otolen; 54 ssize_t len; 55 struct sockaddr_storage ss; 56 struct msghdr m; 57 struct cmsghdr *cm; 58 struct iovec iov[2]; 59 u_char cmsgbuf[256]; 60 struct in6_pktinfo *pi; 61 struct sockaddr_in *sin4; 62 socklen_t sslen; 63 struct sockaddr_in6 *sin6; 64 65 sslen = sizeof(ss); 66 if (getsockname(s, (struct sockaddr *)&ss, &sslen) < 0) 67 return -1; 68 69 m.msg_name = (caddr_t)from; 70 m.msg_namelen = *fromlen; 71 iov[0].iov_base = (caddr_t)buf; 72 iov[0].iov_len = buflen; 73 m.msg_iov = iov; 74 m.msg_iovlen = 1; 75 memset(cmsgbuf, 0, sizeof(cmsgbuf)); 76 cm = (struct cmsghdr *)cmsgbuf; 77 m.msg_control = (caddr_t)cm; 78 m.msg_controllen = sizeof(cmsgbuf); 79 if ((len = recvmsg(s, &m, flags)) <= 0) { 80 return len; 81 } 82 *fromlen = m.msg_namelen; 83 84 otolen = *tolen; 85 *tolen = 0; 86 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m); 87 m.msg_controllen != 0 && cm; 88 cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) { 89 if (ss.ss_family == AF_INET6 90 && cm->cmsg_level == IPPROTO_IPV6 91 && cm->cmsg_type == IPV6_PKTINFO 92 && otolen >= sizeof(*sin6)) { 93 pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); 94 *tolen = sizeof(*sin6); 95 sin6 = (struct sockaddr_in6 *)to; 96 memset(sin6, 0, sizeof(*sin6)); 97 sin6->sin6_family = AF_INET6; 98 #ifndef __linux__ 99 sin6->sin6_len = sizeof(*sin6); 100 #endif 101 memcpy(&sin6->sin6_addr, &pi->ipi6_addr, 102 sizeof(sin6->sin6_addr)); 103 /* XXX other cases, such as site-local? */ 104 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) 105 sin6->sin6_scope_id = pi->ipi6_ifindex; 106 else 107 sin6->sin6_scope_id = 0; 108 sin6->sin6_port = 109 ((struct sockaddr_in6 *)&ss)->sin6_port; 110 otolen = -1; /* "to" already set */ 111 continue; 112 } 113 #ifdef __linux__ 114 if (ss.ss_family == AF_INET 115 && cm->cmsg_level == IPPROTO_IP 116 && cm->cmsg_type == IP_PKTINFO 117 && otolen >= sizeof(sin4)) { 118 struct in_pktinfo *pi = (struct in_pktinfo *)(CMSG_DATA(cm)); 119 *tolen = sizeof(*sin4); 120 sin4 = (struct sockaddr_in *)to; 121 memset(sin4, 0, sizeof(*sin4)); 122 sin4->sin_family = AF_INET; 123 memcpy(&sin4->sin_addr, &pi->ipi_addr, 124 sizeof(sin4->sin_addr)); 125 sin4->sin_port = 126 ((struct sockaddr_in *)&ss)->sin_port; 127 otolen = -1; /* "to" already set */ 128 continue; 129 } 130 #endif 131 #ifdef IPV6_RECVDSTADDR 132 if (ss.ss_family == AF_INET6 133 && cm->cmsg_level == IPPROTO_IPV6 134 && cm->cmsg_type == IPV6_RECVDSTADDR 135 && otolen >= sizeof(*sin6)) { 136 *tolen = sizeof(*sin6); 137 sin6 = (struct sockaddr_in6 *)to; 138 memset(sin6, 0, sizeof(*sin6)); 139 sin6->sin6_family = AF_INET6; 140 sin6->sin6_len = sizeof(*sin6); 141 memcpy(&sin6->sin6_addr, CMSG_DATA(cm), 142 sizeof(sin6->sin6_addr)); 143 sin6->sin6_port = 144 ((struct sockaddr_in6 *)&ss)->sin6_port; 145 otolen = -1; /* "to" already set */ 146 continue; 147 } 148 #endif 149 #ifndef __linux__ 150 if (ss.ss_family == AF_INET 151 && cm->cmsg_level == IPPROTO_IP 152 && cm->cmsg_type == IP_RECVDSTADDR 153 && otolen >= sizeof(*sin4)) { 154 *tolen = sizeof(*sin4); 155 sin4 = (struct sockaddr_in *)to; 156 memset(sin4, 0, sizeof(*sin4)); 157 sin4->sin_family = AF_INET; 158 sin4->sin_len = sizeof(*sin4); 159 memcpy(&sin4->sin_addr, CMSG_DATA(cm), 160 sizeof(sin4->sin_addr)); 161 sin4->sin_port = ((struct sockaddr_in *)&ss)->sin_port; 162 otolen = -1; /* "to" already set */ 163 continue; 164 } 165 #endif 166 } 167 168 return len; 169 } 170