xref: /openbsd/usr.sbin/npppd/common/recvfromto.c (revision 64eba9a2)
1 /*	$OpenBSD: recvfromto.c,v 1.7 2024/02/26 08:25:51 yasuoka 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
recvfromto_nat_t(int s,void * buf,size_t buflen,int flags,struct sockaddr * from,u_int * fromlen,struct sockaddr * to,u_int * tolen,void * ipsec,u_int * ipseclen)44 recvfromto_nat_t(int s, void *buf, size_t buflen, int flags,
45     struct sockaddr *from, u_int *fromlen, struct sockaddr *to, u_int *tolen,
46     void *ipsec, u_int *ipseclen)
47 {
48 	int otolen;
49 	u_int oipseclen = 0;
50 	ssize_t len;
51 	struct sockaddr_storage ss;
52 	struct msghdr m;
53 	struct cmsghdr *cm;
54 	struct iovec iov[2];
55 	u_char cmsgbuf[256];
56 	struct in6_pktinfo *pi;
57 	struct sockaddr_in *sin4;
58 	socklen_t sslen;
59 	struct sockaddr_in6 *sin6;
60 
61 	sslen = sizeof(ss);
62 	if (getsockname(s, (struct sockaddr *)&ss, &sslen) < 0)
63 		return -1;
64 
65 	m.msg_name = (caddr_t)from;
66 	m.msg_namelen = *fromlen;
67 	iov[0].iov_base = (caddr_t)buf;
68 	iov[0].iov_len = buflen;
69 	m.msg_iov = iov;
70 	m.msg_iovlen = 1;
71 	memset(cmsgbuf, 0, sizeof(cmsgbuf));
72 	cm = (struct cmsghdr *)cmsgbuf;
73 	m.msg_control = (caddr_t)cm;
74 	m.msg_controllen = sizeof(cmsgbuf);
75 	if ((len = recvmsg(s, &m, flags)) <= 0) {
76 		return len;
77 	}
78 	*fromlen = m.msg_namelen;
79 
80 	if (ipsec && ipseclen) {
81 		oipseclen = *ipseclen;
82 		*ipseclen = 0;
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 IP_IPSECFLOWINFO
114 		if (ss.ss_family == AF_INET	/* ?? */
115 		 && cm->cmsg_level == IPPROTO_IP
116 		 && cm->cmsg_type == IP_IPSECFLOWINFO
117 		 && oipseclen >= sizeof(u_int32_t)) {
118 			*ipseclen = sizeof(u_int32_t);
119 			memcpy(ipsec, CMSG_DATA(cm), *ipseclen);
120 			continue;
121 		}
122 #endif
123 #ifdef __linux__
124 		if (ss.ss_family == AF_INET
125 		 && cm->cmsg_level == IPPROTO_IP
126 		 && cm->cmsg_type == IP_PKTINFO
127 		 && otolen >= sizeof(sin4)) {
128 			struct in_pktinfo *pi = (struct in_pktinfo *)(CMSG_DATA(cm));
129 			*tolen = sizeof(*sin4);
130 			sin4 = (struct sockaddr_in *)to;
131 			memset(sin4, 0, sizeof(*sin4));
132 			sin4->sin_family = AF_INET;
133 			memcpy(&sin4->sin_addr, &pi->ipi_addr,
134 				sizeof(sin4->sin_addr));
135 			sin4->sin_port =
136 				((struct sockaddr_in *)&ss)->sin_port;
137 			otolen = -1;	/* "to" already set */
138 			continue;
139 		}
140 #endif
141 #ifdef IPV6_RECVDSTADDR
142 		if (ss.ss_family == AF_INET6
143 		      && cm->cmsg_level == IPPROTO_IPV6
144 		      && cm->cmsg_type == IPV6_RECVDSTADDR
145 		      && otolen >= sizeof(*sin6)) {
146 			*tolen = sizeof(*sin6);
147 			sin6 = (struct sockaddr_in6 *)to;
148 			memset(sin6, 0, sizeof(*sin6));
149 			sin6->sin6_family = AF_INET6;
150 			sin6->sin6_len = sizeof(*sin6);
151 			memcpy(&sin6->sin6_addr, CMSG_DATA(cm),
152 				sizeof(sin6->sin6_addr));
153 			sin6->sin6_port =
154 				((struct sockaddr_in6 *)&ss)->sin6_port;
155 			otolen = -1;	/* "to" already set */
156 			continue;
157 		}
158 #endif
159 #ifndef __linux__
160 		if (ss.ss_family == AF_INET
161 		 && cm->cmsg_level == IPPROTO_IP
162 		 && cm->cmsg_type == IP_RECVDSTADDR
163 		 && otolen >= sizeof(*sin4)) {
164 			*tolen = sizeof(*sin4);
165 			sin4 = (struct sockaddr_in *)to;
166 			memset(sin4, 0, sizeof(*sin4));
167 			sin4->sin_family = AF_INET;
168 			sin4->sin_len = sizeof(*sin4);
169 			memcpy(&sin4->sin_addr, CMSG_DATA(cm),
170 				sizeof(sin4->sin_addr));
171 			sin4->sin_port = ((struct sockaddr_in *)&ss)->sin_port;
172 			otolen = -1;	/* "to" already set */
173 			continue;
174 		}
175 #endif
176 	}
177 
178 	return len;
179 }
180 
181 int
recvfromto(int s,void * buf,size_t buflen,int flags,struct sockaddr * from,u_int * fromlen,struct sockaddr * to,u_int * tolen)182 recvfromto(int s, void *buf, size_t buflen, int flags, struct sockaddr *from,
183     u_int *fromlen, struct sockaddr *to, u_int *tolen)
184 {
185 	return recvfromto_nat_t(s, buf, buflen, flags, from, fromlen,
186 	    to, tolen, NULL, NULL);
187 }
188 
189 int
sendto_nat_t(int s,const void * buf,size_t buflen,int flags,struct sockaddr * to,u_int tolen,void * ipsec)190 sendto_nat_t(int s, const void *buf, size_t buflen, int flags,
191     struct sockaddr *to, u_int tolen, void *ipsec)
192 {
193 #ifdef IP_IPSECFLOWINFO
194 	if (ipsec) {
195 		struct iovec iov[1];
196 		struct msghdr msg;
197 		struct cmsghdr  *cmsg;
198 		union {
199 			struct cmsghdr  hdr;
200 			char            buf[CMSG_SPACE(sizeof(u_int32_t))];
201 		} cmsgbuf;
202 
203 		iov[0].iov_base = (char *)buf;
204 		iov[0].iov_len = buflen;
205 		memset(&msg, 0, sizeof(msg));
206 		memset(&cmsgbuf, 0, sizeof(cmsgbuf));
207 		msg.msg_name = to;
208 		msg.msg_namelen = tolen;
209 		msg.msg_iov = iov;
210 		msg.msg_iovlen = 1;
211 		msg.msg_control = (caddr_t)&cmsgbuf.buf;
212 		msg.msg_controllen = sizeof(cmsgbuf.buf);
213 		cmsg = CMSG_FIRSTHDR(&msg);
214 		cmsg->cmsg_len = CMSG_LEN(sizeof(u_int32_t));
215 		cmsg->cmsg_level = IPPROTO_IP;
216 		cmsg->cmsg_type = IP_IPSECFLOWINFO;
217 		memcpy(CMSG_DATA(cmsg), ipsec, sizeof(u_int32_t));
218 		return sendmsg(s, &msg, flags);
219 	}
220 #endif
221 	return sendto(s, buf, buflen, flags, to, tolen);
222 }
223