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)
34 # ifndef HAVE_STRUCT_IN_PKTINFO_IPI_ADDR
36 /* Assume this version of glibc simply lacks the definition */
39 struct in_addr ipi_spec_dst
;
40 struct in_addr ipi_addr
;
43 # undef IP_PKTINFO /* No definition, no way to get it */
49 # define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
52 # define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
56 * Check to see if this is a valid local address, meaning that we can
59 static int address_is_local(const union sock_addr
*addr
)
61 union sock_addr sa1
, sa2
;
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))
73 sa1
.si
.sin_port
= 0; /* Any port */
76 else if (sa1
.sa
.sa_family
== AF_INET6
) {
77 if (IN6_IS_ADDR_MULTICAST(&sa1
.s6
.sin6_addr
))
79 sa1
.s6
.sin6_port
= 0; /* Any port */
85 sockfd
= socket(sa1
.sa
.sa_family
, SOCK_DGRAM
, 0);
89 if (bind(sockfd
, &sa1
.sa
, SOCKLEN(&sa1
)))
92 addrlen
= SOCKLEN(addr
);
93 if (getsockname(sockfd
, (struct sockaddr
*)&sa2
, &addrlen
))
96 if (sa1
.sa
.sa_family
!= sa2
.sa
.sa_family
)
99 if (sa2
.sa
.sa_family
== AF_INET
)
100 rv
= sa1
.si
.sin_addr
.s_addr
== sa2
.si
.sin_addr
.s_addr
;
102 else if (sa2
.sa
.sa_family
== AF_INET6
)
103 rv
= IN6_ARE_ADDR_EQUAL(&sa1
.s6
.sin6_addr
, &sa2
.s6
.sin6_addr
);
118 static void normalize_ip6_compat(union sock_addr
*myaddr
)
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
);
141 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
142 union sock_addr
*from
, union sock_addr
*myaddr
)
147 struct cmsghdr
*cmptr
;
151 char control
[CMSG_SPACE(sizeof(struct in_addr
)) +
152 CMSG_SPACE(sizeof(struct in_pktinfo
))];
154 char control
[CMSG_SPACE(sizeof(struct in_addr
))];
157 #ifdef HAVE_STRUCT_IN6_PKTINFO
158 char control6
[CMSG_SPACE(sizeof(struct in6_addr
)) +
159 CMSG_SPACE(sizeof(struct in6_pktinfo
))];
161 char control6
[CMSG_SPACE(sizeof(struct in6_addr
))];
167 struct in_pktinfo pktinfo
;
169 #ifdef HAVE_STRUCT_IN6_PKTINFO
170 struct in6_pktinfo pktinfo6
;
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
));
179 if (from
->sa
.sa_family
== AF_INET
)
180 setsockopt(s
, IPPROTO_IP
, IP_PKTINFO
, &on
, sizeof(on
));
183 #ifdef IPV6_RECVPKTINFO
184 if (from
->sa
.sa_family
== AF_INET6
)
185 setsockopt(s
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &on
, sizeof(on
));
188 bzero(&msg
, sizeof msg
); /* Clear possible system-dependent fields */
189 msg
.msg_control
= control_un
.control
;
190 msg
.msg_controllen
= sizeof(control_un
);
193 msg
.msg_name
= &from
->sa
;
194 msg
.msg_namelen
= sizeof(*from
);
200 if ((n
= recvmsg(s
, &msg
, flags
)) < 0)
201 return n
; /* Error */
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
));
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
));
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
));
244 #ifdef HAVE_STRUCT_IN6_PKTINFO
245 if (cmptr
->cmsg_level
== IPPROTO_IPV6
&&
247 #ifdef IPV6_RECVPKTINFO
248 cmptr
->cmsg_type
== IPV6_RECVPKTINFO
||
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
));
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
;
270 else if (myaddr
->sa
.sa_family
== AF_INET6
)
271 memset(&myaddr
->s6
.sin6_addr
, 0, sizeof(struct in6_addr
));
276 normalize_ip6_compat(from
);
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
);