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 * ----------------------------------------------------------------------- */
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"
23 #ifdef HAVE_MACHINE_PARAM_H
24 #include <machine/param.h> /* Needed on some versions of FreeBSD */
27 #if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL)
32 # ifndef HAVE_STRUCT_IN_PKTINFO
34 /* Assume this version of glibc simply lacks the definition */
37 struct in_addr ipi_spec_dst
;
38 struct in_addr ipi_addr
;
41 # undef IP_PKTINFO /* No definition, no way to get it */
47 # define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
50 # define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
54 * Check to see if this is a valid local address, meaning that we can
57 static int address_is_local(const union sock_addr
*addr
)
59 union sock_addr sa1
, sa2
;
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))
71 sa1
.si
.sin_port
= 0; /* Any port */
74 else if (sa1
.sa
.sa_family
== AF_INET6
) {
75 if (IN6_IS_ADDR_MULTICAST(&sa1
.s6
.sin6_addr
))
77 sa1
.s6
.sin6_port
= 0; /* Any port */
83 sockfd
= socket(sa1
.sa
.sa_family
, SOCK_DGRAM
, 0);
87 if (bind(sockfd
, &sa1
.sa
, SOCKLEN(&sa1
)))
90 addrlen
= SOCKLEN(addr
);
91 if (getsockname(sockfd
, (struct sockaddr
*)&sa2
, &addrlen
))
94 if (sa1
.sa
.sa_family
!= sa2
.sa
.sa_family
)
97 if (sa2
.sa
.sa_family
== AF_INET
)
98 rv
= sa1
.si
.sin_addr
.s_addr
== sa2
.si
.sin_addr
.s_addr
;
100 else if (sa2
.sa
.sa_family
== AF_INET6
)
101 rv
= IN6_ARE_ADDR_EQUAL(&sa1
.s6
.sin6_addr
, &sa2
.s6
.sin6_addr
);
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
);
136 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
137 struct sockaddr
*from
, socklen_t
* fromlen
,
138 union sock_addr
*myaddr
)
143 struct cmsghdr
*cmptr
;
147 char control
[CMSG_SPACE(sizeof(struct in_addr
)) +
148 CMSG_SPACE(sizeof(struct in_pktinfo
))];
150 char control
[CMSG_SPACE(sizeof(struct in_addr
))];
153 #ifdef HAVE_STRUCT_IN6_PKTINFO
154 char control6
[CMSG_SPACE(sizeof(struct in6_addr
)) +
155 CMSG_SPACE(sizeof(struct in6_pktinfo
))];
157 char control6
[CMSG_SPACE(sizeof(struct in6_addr
))];
163 struct in_pktinfo pktinfo
;
165 #ifdef HAVE_STRUCT_IN6_PKTINFO
166 struct in6_pktinfo pktinfo6
;
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
));
175 if (from
->sa_family
== AF_INET
)
176 setsockopt(s
, IPPROTO_IP
, IP_PKTINFO
, &on
, sizeof(on
));
179 #ifdef IPV6_RECVPKTINFO
180 if (from
->sa_family
== AF_INET6
)
181 setsockopt(s
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &on
, sizeof(on
));
184 bzero(&msg
, sizeof msg
); /* Clear possible system-dependent fields */
185 msg
.msg_control
= control_un
.control
;
186 msg
.msg_controllen
= sizeof(control_un
);
190 msg
.msg_namelen
= *fromlen
;
196 if ((n
= recvmsg(s
, &msg
, flags
)) < 0)
197 return n
; /* Error */
199 *fromlen
= msg
.msg_namelen
;
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
));
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
));
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
));
242 #ifdef HAVE_STRUCT_IN6_PKTINFO
243 if (cmptr
->cmsg_level
== IPPROTO_IPV6
&&
245 #ifdef IPV6_RECVPKTINFO
246 cmptr
->cmsg_type
== IPV6_RECVPKTINFO
||
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
));
255 normalize_ip6_compat(myaddr
);
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
;
266 else if (myaddr
->sa
.sa_family
== AF_INET6
)
267 memset(&myaddr
->s6
.sin6_addr
, 0, sizeof(struct in6_addr
));
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
);