xref: /openbsd/usr.sbin/npppd/common/recvfromto.c (revision cecf84d4)
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_nat_t(s, buf, buflen, flags, from, fromlen, to, tolen,
44     ipsec, ipseclen)
45 	int s;
46 	void *buf;
47 	size_t buflen;
48 	int flags;
49 	struct sockaddr *from;
50 	u_int *fromlen;
51 	struct sockaddr *to;
52 	u_int *tolen;
53 	void *ipsec;
54 	u_int *ipseclen;
55 {
56 	int otolen;
57 	u_int oipseclen = 0;
58 	ssize_t len;
59 	struct sockaddr_storage ss;
60 	struct msghdr m;
61 	struct cmsghdr *cm;
62 	struct iovec iov[2];
63 	u_char cmsgbuf[256];
64 	struct in6_pktinfo *pi;
65 	struct sockaddr_in *sin4;
66 	socklen_t sslen;
67 	struct sockaddr_in6 *sin6;
68 
69 	sslen = sizeof(ss);
70 	if (getsockname(s, (struct sockaddr *)&ss, &sslen) < 0)
71 		return -1;
72 
73 	m.msg_name = (caddr_t)from;
74 	m.msg_namelen = *fromlen;
75 	iov[0].iov_base = (caddr_t)buf;
76 	iov[0].iov_len = buflen;
77 	m.msg_iov = iov;
78 	m.msg_iovlen = 1;
79 	memset(cmsgbuf, 0, sizeof(cmsgbuf));
80 	cm = (struct cmsghdr *)cmsgbuf;
81 	m.msg_control = (caddr_t)cm;
82 	m.msg_controllen = sizeof(cmsgbuf);
83 	if ((len = recvmsg(s, &m, flags)) <= 0) {
84 		return len;
85 	}
86 	*fromlen = m.msg_namelen;
87 
88 	if (ipsec && ipseclen) {
89 		oipseclen = *ipseclen;
90 		*ipseclen = 0;
91 	}
92 	otolen = *tolen;
93 	*tolen = 0;
94 	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
95 	     m.msg_controllen != 0 && cm;
96 	     cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
97 		if (ss.ss_family == AF_INET6
98 		 && cm->cmsg_level == IPPROTO_IPV6
99 		 && cm->cmsg_type == IPV6_PKTINFO
100 		 && otolen >= sizeof(*sin6)) {
101 			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
102 			*tolen = sizeof(*sin6);
103 			sin6 = (struct sockaddr_in6 *)to;
104 			memset(sin6, 0, sizeof(*sin6));
105 			sin6->sin6_family = AF_INET6;
106 #ifndef __linux__
107 			sin6->sin6_len = sizeof(*sin6);
108 #endif
109 			memcpy(&sin6->sin6_addr, &pi->ipi6_addr,
110 				sizeof(sin6->sin6_addr));
111 			/* XXX other cases, such as site-local? */
112 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
113 				sin6->sin6_scope_id = pi->ipi6_ifindex;
114 			else
115 				sin6->sin6_scope_id = 0;
116 			sin6->sin6_port =
117 				((struct sockaddr_in6 *)&ss)->sin6_port;
118 			otolen = -1;	/* "to" already set */
119 			continue;
120 		}
121 #ifdef IP_IPSECFLOWINFO
122 		if (ss.ss_family == AF_INET	/* ?? */
123 		 && cm->cmsg_level == IPPROTO_IP
124 		 && cm->cmsg_type == IP_IPSECFLOWINFO
125 		 && oipseclen >= sizeof(u_int32_t)) {
126 			*ipseclen = sizeof(u_int32_t);
127 			memcpy(ipsec, CMSG_DATA(cm), *ipseclen);
128 			continue;
129 		}
130 #endif
131 #ifdef __linux__
132 		if (ss.ss_family == AF_INET
133 		 && cm->cmsg_level == IPPROTO_IP
134 		 && cm->cmsg_type == IP_PKTINFO
135 		 && otolen >= sizeof(sin4)) {
136 			struct in_pktinfo *pi = (struct in_pktinfo *)(CMSG_DATA(cm));
137 			*tolen = sizeof(*sin4);
138 			sin4 = (struct sockaddr_in *)to;
139 			memset(sin4, 0, sizeof(*sin4));
140 			sin4->sin_family = AF_INET;
141 			memcpy(&sin4->sin_addr, &pi->ipi_addr,
142 				sizeof(sin4->sin_addr));
143 			sin4->sin_port =
144 				((struct sockaddr_in *)&ss)->sin_port;
145 			otolen = -1;	/* "to" already set */
146 			continue;
147 		}
148 #endif
149 #ifdef IPV6_RECVDSTADDR
150 		if (ss.ss_family == AF_INET6
151 		      && cm->cmsg_level == IPPROTO_IPV6
152 		      && cm->cmsg_type == IPV6_RECVDSTADDR
153 		      && otolen >= sizeof(*sin6)) {
154 			*tolen = sizeof(*sin6);
155 			sin6 = (struct sockaddr_in6 *)to;
156 			memset(sin6, 0, sizeof(*sin6));
157 			sin6->sin6_family = AF_INET6;
158 			sin6->sin6_len = sizeof(*sin6);
159 			memcpy(&sin6->sin6_addr, CMSG_DATA(cm),
160 				sizeof(sin6->sin6_addr));
161 			sin6->sin6_port =
162 				((struct sockaddr_in6 *)&ss)->sin6_port;
163 			otolen = -1;	/* "to" already set */
164 			continue;
165 		}
166 #endif
167 #ifndef __linux__
168 		if (ss.ss_family == AF_INET
169 		 && cm->cmsg_level == IPPROTO_IP
170 		 && cm->cmsg_type == IP_RECVDSTADDR
171 		 && otolen >= sizeof(*sin4)) {
172 			*tolen = sizeof(*sin4);
173 			sin4 = (struct sockaddr_in *)to;
174 			memset(sin4, 0, sizeof(*sin4));
175 			sin4->sin_family = AF_INET;
176 			sin4->sin_len = sizeof(*sin4);
177 			memcpy(&sin4->sin_addr, CMSG_DATA(cm),
178 				sizeof(sin4->sin_addr));
179 			sin4->sin_port = ((struct sockaddr_in *)&ss)->sin_port;
180 			otolen = -1;	/* "to" already set */
181 			continue;
182 		}
183 #endif
184 	}
185 
186 	return len;
187 }
188 
189 int
190 recvfromto(s, buf, buflen, flags, from, fromlen, to, tolen)
191 	int s;
192 	void *buf;
193 	size_t buflen;
194 	int flags;
195 	struct sockaddr *from;
196 	u_int *fromlen;
197 	struct sockaddr *to;
198 	u_int *tolen;
199 {
200 	return recvfromto_nat_t(s, buf, buflen, flags, from, fromlen,
201 	    to, tolen, NULL, NULL);
202 }
203 
204 int
205 sendto_nat_t(s, buf, buflen, flags, to, tolen, ipsec)
206 	int s;
207 	const void *buf;
208 	size_t buflen;
209 	int flags;
210 	struct sockaddr *to;
211 	u_int tolen;
212 	void *ipsec;
213 {
214 #ifdef IP_IPSECFLOWINFO
215 	if (ipsec) {
216 		struct iovec iov[1];
217 		struct msghdr msg;
218 		struct cmsghdr  *cmsg;
219 		union {
220 			struct cmsghdr  hdr;
221 			char            buf[CMSG_SPACE(sizeof(u_int32_t))];
222 		} cmsgbuf;
223 
224 		iov[0].iov_base = (char *)buf;
225 		iov[0].iov_len = buflen;
226 		memset(&msg, 0, sizeof(msg));
227 		memset(&cmsgbuf, 0, sizeof(cmsgbuf));
228 		msg.msg_name = to;
229 		msg.msg_namelen = tolen;
230 		msg.msg_iov = iov;
231 		msg.msg_iovlen = 1;
232 		msg.msg_control = (caddr_t)&cmsgbuf.buf;
233 		msg.msg_controllen = sizeof(cmsgbuf.buf);
234 		cmsg = CMSG_FIRSTHDR(&msg);
235 		cmsg->cmsg_len = CMSG_LEN(sizeof(u_int32_t));
236 		cmsg->cmsg_level = IPPROTO_IP;
237 		cmsg->cmsg_type = IP_IPSECFLOWINFO;
238 		memcpy(CMSG_DATA(cmsg), ipsec, sizeof(u_int32_t));
239 		return sendmsg(s, &msg, flags);
240 	}
241 #endif
242 	return sendto(s, buf, buflen, flags, to, tolen);
243 }
244