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 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
118 struct sockaddr
*from
, socklen_t
* fromlen
,
119 union sock_addr
*myaddr
)
124 struct cmsghdr
*cmptr
;
128 char control
[CMSG_SPACE(sizeof(struct in_addr
)) +
129 CMSG_SPACE(sizeof(struct in_pktinfo
))];
131 char control
[CMSG_SPACE(sizeof(struct in_addr
))];
134 #ifdef HAVE_STRUCT_IN6_PKTINFO
135 char control6
[CMSG_SPACE(sizeof(struct in6_addr
)) +
136 CMSG_SPACE(sizeof(struct in6_pktinfo
))];
138 char control6
[CMSG_SPACE(sizeof(struct in6_addr
))];
144 struct in_pktinfo pktinfo
;
146 #ifdef HAVE_STRUCT_IN6_PKTINFO
147 struct in6_pktinfo pktinfo6
;
150 /* Try to enable getting the return address */
151 #ifdef IP_RECVDSTADDR
152 if (from
->sa_family
== AF_INET
)
153 setsockopt(s
, IPPROTO_IP
, IP_RECVDSTADDR
, &on
, sizeof(on
));
156 if (from
->sa_family
== AF_INET
)
157 setsockopt(s
, IPPROTO_IP
, IP_PKTINFO
, &on
, sizeof(on
));
160 #ifdef IPV6_RECVPKTINFO
161 if (from
->sa_family
== AF_INET6
)
162 setsockopt(s
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &on
, sizeof(on
));
165 bzero(&msg
, sizeof msg
); /* Clear possible system-dependent fields */
166 msg
.msg_control
= control_un
.control
;
167 msg
.msg_controllen
= sizeof(control_un
);
171 msg
.msg_namelen
= *fromlen
;
177 if ((n
= recvmsg(s
, &msg
, flags
)) < 0)
178 return n
; /* Error */
180 *fromlen
= msg
.msg_namelen
;
183 bzero(myaddr
, sizeof(*myaddr
));
184 myaddr
->sa
.sa_family
= from
->sa_family
;
186 if (msg
.msg_controllen
< sizeof(struct cmsghdr
) ||
187 (msg
.msg_flags
& MSG_CTRUNC
))
188 return n
; /* No information available */
190 for (cmptr
= CMSG_FIRSTHDR(&msg
); cmptr
!= NULL
;
191 cmptr
= CMSG_NXTHDR(&msg
, cmptr
)) {
193 if (from
->sa_family
== AF_INET
) {
194 myaddr
->sa
.sa_family
= AF_INET
;
195 #ifdef IP_RECVDSTADDR
196 if (cmptr
->cmsg_level
== IPPROTO_IP
&&
197 cmptr
->cmsg_type
== IP_RECVDSTADDR
) {
198 memcpy(&myaddr
->si
.sin_addr
, CMSG_DATA(cmptr
),
199 sizeof(struct in_addr
));
204 if (cmptr
->cmsg_level
== IPPROTO_IP
&&
205 cmptr
->cmsg_type
== IP_PKTINFO
) {
206 memcpy(&pktinfo
, CMSG_DATA(cmptr
),
207 sizeof(struct in_pktinfo
));
208 memcpy(&myaddr
->si
.sin_addr
, &pktinfo
.ipi_addr
,
209 sizeof(struct in_addr
));
214 else if (from
->sa_family
== AF_INET6
) {
215 myaddr
->sa
.sa_family
= AF_INET6
;
216 #ifdef IP6_RECVDSTADDR
217 if (cmptr
->cmsg_level
== IPPROTO_IPV6
&&
218 cmptr
->cmsg_type
== IPV6_RECVDSTADDR
)
219 memcpy(&myaddr
->s6
.sin6_addr
, CMSG_DATA(cmptr
),
220 sizeof(struct in6_addr
));
223 #ifdef HAVE_STRUCT_IN6_PKTINFO
224 if (cmptr
->cmsg_level
== IPPROTO_IPV6
&&
226 #ifdef IPV6_RECVPKTINFO
227 cmptr
->cmsg_type
== IPV6_RECVPKTINFO
||
229 cmptr
->cmsg_type
== IPV6_PKTINFO
)) {
230 memcpy(&pktinfo6
, CMSG_DATA(cmptr
),
231 sizeof(struct in6_pktinfo
));
232 memcpy(&myaddr
->s6
.sin6_addr
, &pktinfo6
.ipi6_addr
,
233 sizeof(struct in6_addr
));
239 /* If the address is not a valid local address,
240 * then bind to any address...
242 if (address_is_local(myaddr
) != 1) {
243 if (myaddr
->sa
.sa_family
== AF_INET
)
244 ((struct sockaddr_in
*)myaddr
)->sin_addr
.s_addr
= INADDR_ANY
;
246 else if (myaddr
->sa
.sa_family
== AF_INET6
)
247 memset(&myaddr
->s6
.sin6_addr
, 0, sizeof(struct in6_addr
));
254 #else /* pointless... */
257 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
258 struct sockaddr
*from
, socklen_t
* fromlen
,
259 union sock_addr
*myaddr
)
261 /* There is no way we can get the local address, fudge it */
263 bzero(myaddr
, sizeof(*myaddr
));
264 myaddr
->sa
.sa_family
= from
->sa_family
;
265 sa_set_port(myaddr
, htons(IPPORT_TFTP
));
267 return recvfrom(s
, buf
, len
, flags
, from
, fromlen
);