tftp: convert IPv6-mapped IPv4 addresses to IPv4
[tftp-hpa.git] / tftpd / recvfrom.c
blobb61382300860595bce4393b13c1ff1a1691bb595
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 #include <sys/uio.h>
31 #ifdef IP_PKTINFO
32 # ifndef HAVE_STRUCT_IN_PKTINFO
33 # ifdef __linux__
34 /* Assume this version of glibc simply lacks the definition */
35 struct in_pktinfo {
36 int ipi_ifindex;
37 struct in_addr ipi_spec_dst;
38 struct in_addr ipi_addr;
40 # else
41 # undef IP_PKTINFO /* No definition, no way to get it */
42 # endif
43 # endif
44 #endif
46 #ifndef CMSG_LEN
47 # define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
48 #endif
49 #ifndef CMSG_SPACE
50 # define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
51 #endif
54 * Check to see if this is a valid local address, meaning that we can
55 * legally bind to it.
57 static int address_is_local(const union sock_addr *addr)
59 union sock_addr sa1, sa2;
60 int sockfd = -1;
61 int e;
62 int rv = 0;
63 socklen_t addrlen;
65 memcpy(&sa1, addr, sizeof sa1);
67 /* Multicast or universal broadcast address? */
68 if (sa1.sa.sa_family == AF_INET) {
69 if (ntohl(sa1.si.sin_addr.s_addr) >= (224UL << 24))
70 return 0;
71 sa1.si.sin_port = 0; /* Any port */
73 #ifdef HAVE_IPV6
74 else if (sa1.sa.sa_family == AF_INET6) {
75 if (IN6_IS_ADDR_MULTICAST(&sa1.s6.sin6_addr))
76 return 0;
77 sa1.s6.sin6_port = 0; /* Any port */
79 #endif
80 else
81 return 0;
83 sockfd = socket(sa1.sa.sa_family, SOCK_DGRAM, 0);
84 if (sockfd < 0)
85 goto err;
87 if (bind(sockfd, &sa1.sa, SOCKLEN(&sa1)))
88 goto err;
90 addrlen = SOCKLEN(addr);
91 if (getsockname(sockfd, (struct sockaddr *)&sa2, &addrlen))
92 goto err;
94 if (sa1.sa.sa_family != sa2.sa.sa_family)
95 goto err;
97 if (sa2.sa.sa_family == AF_INET)
98 rv = sa1.si.sin_addr.s_addr == sa2.si.sin_addr.s_addr;
99 #ifdef HAVE_IPV6
100 else if (sa2.sa.sa_family == AF_INET6)
101 rv = IN6_ARE_ADDR_EQUAL(&sa1.s6.sin6_addr, &sa2.s6.sin6_addr);
102 #endif
103 else
104 rv = 0;
106 err:
107 e = errno;
109 if (sockfd >= 0)
110 close(sockfd);
112 errno = e;
113 return rv;
116 #ifdef HAVE_IPV6
117 static void normalize_ip6_compat(union sock_addr *myaddr)
119 static const uint8_t ip6_compat_prefix[12] =
120 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
121 struct sockaddr_in in;
123 if (!memcmp(&myaddr->s6.sin6_addr, ip6_compat_prefix,
124 sizeof ip6_compat_prefix)) {
125 bzero(&in, sizeof in);
126 in.sin_family = AF_INET;
127 in.sin_port = myaddr->s6.sin6_port;
128 memcpy(&in.sin_addr, (const char *)&myaddr->s6.sin6_addr +
129 sizeof ip6_compat_prefix, sizeof in.sin_addr);
130 memcpy(&myaddr->si, &in, sizeof in);
133 #endif
136 myrecvfrom(int s, void *buf, int len, unsigned int flags,
137 struct sockaddr *from, socklen_t * fromlen,
138 union sock_addr *myaddr)
140 struct msghdr msg;
141 struct iovec iov;
142 int n;
143 struct cmsghdr *cmptr;
144 union {
145 struct cmsghdr cm;
146 #ifdef IP_PKTINFO
147 char control[CMSG_SPACE(sizeof(struct in_addr)) +
148 CMSG_SPACE(sizeof(struct in_pktinfo))];
149 #else
150 char control[CMSG_SPACE(sizeof(struct in_addr))];
151 #endif
152 #ifdef HAVE_IPV6
153 #ifdef HAVE_STRUCT_IN6_PKTINFO
154 char control6[CMSG_SPACE(sizeof(struct in6_addr)) +
155 CMSG_SPACE(sizeof(struct in6_pktinfo))];
156 #else
157 char control6[CMSG_SPACE(sizeof(struct in6_addr))];
158 #endif
159 #endif
160 } control_un;
161 int on = 1;
162 #ifdef IP_PKTINFO
163 struct in_pktinfo pktinfo;
164 #endif
165 #ifdef HAVE_STRUCT_IN6_PKTINFO
166 struct in6_pktinfo pktinfo6;
167 #endif
169 /* Try to enable getting the return address */
170 #ifdef IP_RECVDSTADDR
171 if (from->sa_family == AF_INET)
172 setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
173 #endif
174 #ifdef IP_PKTINFO
175 if (from->sa_family == AF_INET)
176 setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
177 #endif
178 #ifdef HAVE_IPV6
179 #ifdef IPV6_RECVPKTINFO
180 if (from->sa_family == AF_INET6)
181 setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
182 #endif
183 #endif
184 bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */
185 msg.msg_control = control_un.control;
186 msg.msg_controllen = sizeof(control_un);
187 msg.msg_flags = 0;
189 msg.msg_name = from;
190 msg.msg_namelen = *fromlen;
191 iov.iov_base = buf;
192 iov.iov_len = len;
193 msg.msg_iov = &iov;
194 msg.msg_iovlen = 1;
196 if ((n = recvmsg(s, &msg, flags)) < 0)
197 return n; /* Error */
199 *fromlen = msg.msg_namelen;
201 if (myaddr) {
202 bzero(myaddr, sizeof(*myaddr));
203 myaddr->sa.sa_family = from->sa_family;
205 if (msg.msg_controllen < sizeof(struct cmsghdr) ||
206 (msg.msg_flags & MSG_CTRUNC))
207 return n; /* No information available */
209 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
210 cmptr = CMSG_NXTHDR(&msg, cmptr)) {
212 if (from->sa_family == AF_INET) {
213 myaddr->sa.sa_family = AF_INET;
214 #ifdef IP_RECVDSTADDR
215 if (cmptr->cmsg_level == IPPROTO_IP &&
216 cmptr->cmsg_type == IP_RECVDSTADDR) {
217 memcpy(&myaddr->si.sin_addr, CMSG_DATA(cmptr),
218 sizeof(struct in_addr));
220 #endif
222 #ifdef IP_PKTINFO
223 if (cmptr->cmsg_level == IPPROTO_IP &&
224 cmptr->cmsg_type == IP_PKTINFO) {
225 memcpy(&pktinfo, CMSG_DATA(cmptr),
226 sizeof(struct in_pktinfo));
227 memcpy(&myaddr->si.sin_addr, &pktinfo.ipi_addr,
228 sizeof(struct in_addr));
230 #endif
232 #ifdef HAVE_IPV6
233 else if (from->sa_family == AF_INET6) {
234 myaddr->sa.sa_family = AF_INET6;
235 #ifdef IP6_RECVDSTADDR
236 if (cmptr->cmsg_level == IPPROTO_IPV6 &&
237 cmptr->cmsg_type == IPV6_RECVDSTADDR )
238 memcpy(&myaddr->s6.sin6_addr, CMSG_DATA(cmptr),
239 sizeof(struct in6_addr));
240 #endif
242 #ifdef HAVE_STRUCT_IN6_PKTINFO
243 if (cmptr->cmsg_level == IPPROTO_IPV6 &&
245 #ifdef IPV6_RECVPKTINFO
246 cmptr->cmsg_type == IPV6_RECVPKTINFO ||
247 #endif
248 cmptr->cmsg_type == IPV6_PKTINFO)) {
249 memcpy(&pktinfo6, CMSG_DATA(cmptr),
250 sizeof(struct in6_pktinfo));
251 memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr,
252 sizeof(struct in6_addr));
254 #endif
255 normalize_ip6_compat(myaddr);
257 #endif
259 /* If the address is not a valid local address,
260 * then bind to any address...
262 if (address_is_local(myaddr) != 1) {
263 if (myaddr->sa.sa_family == AF_INET)
264 ((struct sockaddr_in *)myaddr)->sin_addr.s_addr = INADDR_ANY;
265 #ifdef HAVE_IPV6
266 else if (myaddr->sa.sa_family == AF_INET6)
267 memset(&myaddr->s6.sin6_addr, 0, sizeof(struct in6_addr));
268 #endif
271 return n;
274 #else /* pointless... */
277 myrecvfrom(int s, void *buf, int len, unsigned int flags,
278 struct sockaddr *from, socklen_t * fromlen,
279 union sock_addr *myaddr)
281 /* There is no way we can get the local address, fudge it */
283 bzero(myaddr, sizeof(*myaddr));
284 myaddr->sa.sa_family = from->sa_family;
285 sa_set_port(myaddr, htons(IPPORT_TFTP));
287 return recvfrom(s, buf, len, flags, from, fromlen);
290 #endif