remap: *actually* build, and fix masked logic errors
[tftp-hpa.git] / tftpd / recvfrom.c
blob320678d1b132af0cdd7e1eb7640b7fbf7089f337
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved
5 * This program is free software available under the same license
6 * as the "OpenBSD" operating system, distributed at
7 * http://www.openbsd.org/.
9 * ----------------------------------------------------------------------- */
12 * recvfrom.c
14 * Emulate recvfrom() using recvmsg(), but try to capture the local address
15 * since some TFTP clients consider it an error to get the reply from another
16 * IP address than the request was sent to.
20 #include "config.h" /* Must be included first! */
21 #include "common/tftpsubs.h"
22 #include "recvfrom.h"
23 #ifdef HAVE_MACHINE_PARAM_H
24 #include <machine/param.h> /* Needed on some versions of FreeBSD */
25 #endif
27 #if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL)
29 #ifdef HAVE_SYS_UIO_H
30 # include <sys/uio.h>
31 #endif
33 #ifdef IP_PKTINFO
34 # ifndef HAVE_STRUCT_IN_PKTINFO_IPI_ADDR
35 # ifdef __linux__
36 /* Assume this version of glibc simply lacks the definition */
37 struct in_pktinfo {
38 int ipi_ifindex;
39 struct in_addr ipi_spec_dst;
40 struct in_addr ipi_addr;
42 # else
43 # undef IP_PKTINFO /* No definition, no way to get it */
44 # endif
45 # endif
46 #endif
48 #ifndef CMSG_LEN
49 # define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
50 #endif
51 #ifndef CMSG_SPACE
52 # define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
53 #endif
56 * Check to see if this is a valid local address, meaning that we can
57 * legally bind to it.
59 static int address_is_local(const union sock_addr *addr)
61 union sock_addr sa1, sa2;
62 int sockfd = -1;
63 int e;
64 int rv = 0;
65 socklen_t addrlen;
67 memcpy(&sa1, addr, sizeof sa1);
69 /* Multicast or universal broadcast address? */
70 if (sa1.sa.sa_family == AF_INET) {
71 if (ntohl(sa1.si.sin_addr.s_addr) >= (224UL << 24))
72 return 0;
73 sa1.si.sin_port = 0; /* Any port */
75 #ifdef HAVE_IPV6
76 else if (sa1.sa.sa_family == AF_INET6) {
77 if (IN6_IS_ADDR_MULTICAST(&sa1.s6.sin6_addr))
78 return 0;
79 sa1.s6.sin6_port = 0; /* Any port */
81 #endif
82 else
83 return 0;
85 sockfd = socket(sa1.sa.sa_family, SOCK_DGRAM, 0);
86 if (sockfd < 0)
87 goto err;
89 if (bind(sockfd, &sa1.sa, SOCKLEN(&sa1)))
90 goto err;
92 addrlen = SOCKLEN(addr);
93 if (getsockname(sockfd, (struct sockaddr *)&sa2, &addrlen))
94 goto err;
96 if (sa1.sa.sa_family != sa2.sa.sa_family)
97 goto err;
99 if (sa2.sa.sa_family == AF_INET)
100 rv = sa1.si.sin_addr.s_addr == sa2.si.sin_addr.s_addr;
101 #ifdef HAVE_IPV6
102 else if (sa2.sa.sa_family == AF_INET6)
103 rv = IN6_ARE_ADDR_EQUAL(&sa1.s6.sin6_addr, &sa2.s6.sin6_addr);
104 #endif
105 else
106 rv = 0;
108 err:
109 e = errno;
111 if (sockfd >= 0)
112 close(sockfd);
114 errno = e;
115 return rv;
118 static void normalize_ip6_compat(union sock_addr *myaddr)
120 #ifdef HAVE_IPV6
121 static const uint8_t ip6_compat_prefix[12] =
122 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
123 struct sockaddr_in in;
125 if (myaddr->sa.sa_family == AF_INET6 &&
126 !memcmp(&myaddr->s6.sin6_addr, ip6_compat_prefix,
127 sizeof ip6_compat_prefix)) {
128 bzero(&in, sizeof in);
129 in.sin_family = AF_INET;
130 in.sin_port = myaddr->s6.sin6_port;
131 memcpy(&in.sin_addr, (const char *)&myaddr->s6.sin6_addr +
132 sizeof ip6_compat_prefix, sizeof in.sin_addr);
133 memcpy(&myaddr->si, &in, sizeof in);
135 #else
136 (void)myaddr;
137 #endif
141 myrecvfrom(int s, void *buf, int len, unsigned int flags,
142 union sock_addr *from, union sock_addr *myaddr)
144 struct msghdr msg;
145 struct iovec iov;
146 int n;
147 struct cmsghdr *cmptr;
148 union {
149 struct cmsghdr cm;
150 #ifdef IP_PKTINFO
151 char control[CMSG_SPACE(sizeof(struct in_addr)) +
152 CMSG_SPACE(sizeof(struct in_pktinfo))];
153 #else
154 char control[CMSG_SPACE(sizeof(struct in_addr))];
155 #endif
156 #ifdef HAVE_IPV6
157 #ifdef HAVE_STRUCT_IN6_PKTINFO
158 char control6[CMSG_SPACE(sizeof(struct in6_addr)) +
159 CMSG_SPACE(sizeof(struct in6_pktinfo))];
160 #else
161 char control6[CMSG_SPACE(sizeof(struct in6_addr))];
162 #endif
163 #endif
164 } control_un;
165 int on = 1;
166 #ifdef IP_PKTINFO
167 struct in_pktinfo pktinfo;
168 #endif
169 #ifdef HAVE_STRUCT_IN6_PKTINFO
170 struct in6_pktinfo pktinfo6;
171 #endif
173 /* Try to enable getting the return address */
174 #ifdef IP_RECVDSTADDR
175 if (from->sa.sa_family == AF_INET)
176 setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
177 #endif
178 #ifdef IP_PKTINFO
179 if (from->sa.sa_family == AF_INET)
180 setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
181 #endif
182 #ifdef HAVE_IPV6
183 #ifdef IPV6_RECVPKTINFO
184 if (from->sa.sa_family == AF_INET6)
185 setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
186 #endif
187 #endif
188 bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */
189 msg.msg_control = control_un.control;
190 msg.msg_controllen = sizeof(control_un);
191 msg.msg_flags = 0;
193 msg.msg_name = &from->sa;
194 msg.msg_namelen = sizeof(*from);
195 iov.iov_base = buf;
196 iov.iov_len = len;
197 msg.msg_iov = &iov;
198 msg.msg_iovlen = 1;
200 if ((n = recvmsg(s, &msg, flags)) < 0)
201 return n; /* Error */
203 if (myaddr) {
204 bzero(myaddr, sizeof(*myaddr));
205 myaddr->sa.sa_family = from->sa.sa_family;
207 if (msg.msg_controllen < sizeof(struct cmsghdr) ||
208 (msg.msg_flags & MSG_CTRUNC))
209 return n; /* No information available */
211 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
212 cmptr = CMSG_NXTHDR(&msg, cmptr)) {
214 if (from->sa.sa_family == AF_INET) {
215 myaddr->sa.sa_family = AF_INET;
216 #ifdef IP_RECVDSTADDR
217 if (cmptr->cmsg_level == IPPROTO_IP &&
218 cmptr->cmsg_type == IP_RECVDSTADDR) {
219 memcpy(&myaddr->si.sin_addr, CMSG_DATA(cmptr),
220 sizeof(struct in_addr));
222 #endif
224 #ifdef IP_PKTINFO
225 if (cmptr->cmsg_level == IPPROTO_IP &&
226 cmptr->cmsg_type == IP_PKTINFO) {
227 memcpy(&pktinfo, CMSG_DATA(cmptr),
228 sizeof(struct in_pktinfo));
229 memcpy(&myaddr->si.sin_addr, &pktinfo.ipi_addr,
230 sizeof(struct in_addr));
232 #endif
234 #ifdef HAVE_IPV6
235 else if (from->sa.sa_family == AF_INET6) {
236 myaddr->sa.sa_family = AF_INET6;
237 #ifdef IP6_RECVDSTADDR
238 if (cmptr->cmsg_level == IPPROTO_IPV6 &&
239 cmptr->cmsg_type == IPV6_RECVDSTADDR )
240 memcpy(&myaddr->s6.sin6_addr, CMSG_DATA(cmptr),
241 sizeof(struct in6_addr));
242 #endif
244 #ifdef HAVE_STRUCT_IN6_PKTINFO
245 if (cmptr->cmsg_level == IPPROTO_IPV6 &&
247 #ifdef IPV6_RECVPKTINFO
248 cmptr->cmsg_type == IPV6_RECVPKTINFO ||
249 #endif
250 cmptr->cmsg_type == IPV6_PKTINFO)) {
251 memcpy(&pktinfo6, CMSG_DATA(cmptr),
252 sizeof(struct in6_pktinfo));
253 memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr,
254 sizeof(struct in6_addr));
256 #endif
258 #endif
261 normalize_ip6_compat(myaddr);
263 /* If the address is not a valid local address,
264 * then bind to any address...
266 if (address_is_local(myaddr) != 1) {
267 if (myaddr->sa.sa_family == AF_INET)
268 ((struct sockaddr_in *)myaddr)->sin_addr.s_addr = INADDR_ANY;
269 #ifdef HAVE_IPV6
270 else if (myaddr->sa.sa_family == AF_INET6)
271 memset(&myaddr->s6.sin6_addr, 0, sizeof(struct in6_addr));
272 #endif
276 normalize_ip6_compat(from);
278 return n;
281 #else /* pointless... */
284 myrecvfrom(int s, void *buf, int len, unsigned int flags,
285 union sock_addr *from, union sock_addr *myaddr)
287 /* There is no way we can get the local address, fudge it */
288 socklen_t fromlen = sizeof(*from);
290 bzero(myaddr, sizeof(*myaddr));
291 myaddr->sa.sa_family = from->sa.sa_family;
292 sa_set_port(myaddr, htons(IPPORT_TFTP));
294 return recvfrom(s, buf, len, flags, &from->sa, &fromlen);
297 #endif