xref: /openbsd/usr.sbin/npppd/common/recvfromto.c (revision 8932bfb7)
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