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
);
116 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 (myaddr
->sa
.sa_family
== AF_INET6
&&
124 !memcmp(&myaddr
->s6
.sin6_addr
, ip6_compat_prefix
,
125 sizeof ip6_compat_prefix
)) {
126 bzero(&in
, sizeof in
);
127 in
.sin_family
= AF_INET
;
128 in
.sin_port
= myaddr
->s6
.sin6_port
;
129 memcpy(&in
.sin_addr
, (const char *)&myaddr
->s6
.sin6_addr
+
130 sizeof ip6_compat_prefix
, sizeof in
.sin_addr
);
131 memcpy(&myaddr
->si
, &in
, sizeof in
);
139 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
140 union sock_addr
*from
, union sock_addr
*myaddr
)
145 struct cmsghdr
*cmptr
;
149 char control
[CMSG_SPACE(sizeof(struct in_addr
)) +
150 CMSG_SPACE(sizeof(struct in_pktinfo
))];
152 char control
[CMSG_SPACE(sizeof(struct in_addr
))];
155 #ifdef HAVE_STRUCT_IN6_PKTINFO
156 char control6
[CMSG_SPACE(sizeof(struct in6_addr
)) +
157 CMSG_SPACE(sizeof(struct in6_pktinfo
))];
159 char control6
[CMSG_SPACE(sizeof(struct in6_addr
))];
165 struct in_pktinfo pktinfo
;
167 #ifdef HAVE_STRUCT_IN6_PKTINFO
168 struct in6_pktinfo pktinfo6
;
171 /* Try to enable getting the return address */
172 #ifdef IP_RECVDSTADDR
173 if (from
->sa
.sa_family
== AF_INET
)
174 setsockopt(s
, IPPROTO_IP
, IP_RECVDSTADDR
, &on
, sizeof(on
));
177 if (from
->sa
.sa_family
== AF_INET
)
178 setsockopt(s
, IPPROTO_IP
, IP_PKTINFO
, &on
, sizeof(on
));
181 #ifdef IPV6_RECVPKTINFO
182 if (from
->sa
.sa_family
== AF_INET6
)
183 setsockopt(s
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &on
, sizeof(on
));
186 bzero(&msg
, sizeof msg
); /* Clear possible system-dependent fields */
187 msg
.msg_control
= control_un
.control
;
188 msg
.msg_controllen
= sizeof(control_un
);
191 msg
.msg_name
= &from
->sa
;
192 msg
.msg_namelen
= sizeof(*from
);
198 if ((n
= recvmsg(s
, &msg
, flags
)) < 0)
199 return n
; /* Error */
202 bzero(myaddr
, sizeof(*myaddr
));
203 myaddr
->sa
.sa_family
= from
->sa
.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
.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
.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
));
259 normalize_ip6_compat(myaddr
);
261 /* If the address is not a valid local address,
262 * then bind to any address...
264 if (address_is_local(myaddr
) != 1) {
265 if (myaddr
->sa
.sa_family
== AF_INET
)
266 ((struct sockaddr_in
*)myaddr
)->sin_addr
.s_addr
= INADDR_ANY
;
268 else if (myaddr
->sa
.sa_family
== AF_INET6
)
269 memset(&myaddr
->s6
.sin6_addr
, 0, sizeof(struct in6_addr
));
274 normalize_ip6_compat(from
);
279 #else /* pointless... */
282 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
283 union sock_addr
*from
, union sock_addr
*myaddr
)
285 /* There is no way we can get the local address, fudge it */
286 socklen_t fromlen
= sizeof(*from
);
288 bzero(myaddr
, sizeof(*myaddr
));
289 myaddr
->sa
.sa_family
= from
->sa
.sa_family
;
290 sa_set_port(myaddr
, htons(IPPORT_TFTP
));
292 return recvfrom(s
, buf
, len
, flags
, &from
->sa
, &fromlen
);