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