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. If so, we should
55 * end up having the same local and remote address when trying to
58 static int address_is_local(const union sock_addr
*addr
)
66 /* Multicast or universal broadcast address? */
67 if (addr
->sa
.sa_family
== AF_INET
) {
68 if (ntohl(addr
->si
.sin_addr
.s_addr
) >= (224UL << 24))
72 else if (addr
->sa
.sa_family
== AF_INET6
) {
73 if (IN6_IS_ADDR_MULTICAST(&addr
->s6
.sin6_addr
))
80 sockfd
= socket(addr
->sa
.sa_family
, SOCK_DGRAM
, 0);
84 if (connect(sockfd
, &addr
->sa
, SOCKLEN(addr
)))
87 addrlen
= SOCKLEN(addr
);
88 if (getsockname(sockfd
, (struct sockaddr
*)&sa
, &addrlen
))
91 if (addr
->sa
.sa_family
== AF_INET
)
92 rv
= sa
.si
.sin_addr
.s_addr
== addr
->si
.sin_addr
.s_addr
;
94 else if (addr
->sa
.sa_family
== AF_INET6
)
95 rv
= IN6_ARE_ADDR_EQUAL(&sa
.s6
.sin6_addr
, &addr
->s6
.sin6_addr
);
110 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
111 struct sockaddr
*from
, socklen_t
* fromlen
,
112 union sock_addr
*myaddr
)
117 struct cmsghdr
*cmptr
;
121 char control
[CMSG_SPACE(sizeof(struct in_addr
)) +
122 CMSG_SPACE(sizeof(struct in_pktinfo
))];
124 char control
[CMSG_SPACE(sizeof(struct in_addr
))];
127 #ifdef HAVE_STRUCT_IN6_PKTINFO
128 char control6
[CMSG_SPACE(sizeof(struct in6_addr
)) +
129 CMSG_SPACE(sizeof(struct in6_pktinfo
))];
131 char control6
[CMSG_SPACE(sizeof(struct in6_addr
))];
137 struct in_pktinfo pktinfo
;
139 #ifdef HAVE_STRUCT_IN6_PKTINFO
140 struct in6_pktinfo pktinfo6
;
143 /* Try to enable getting the return address */
144 #ifdef IP_RECVDSTADDR
145 if (from
->sa_family
== AF_INET
)
146 setsockopt(s
, IPPROTO_IP
, IP_RECVDSTADDR
, &on
, sizeof(on
));
149 if (from
->sa_family
== AF_INET
)
150 setsockopt(s
, IPPROTO_IP
, IP_PKTINFO
, &on
, sizeof(on
));
153 #ifdef IPV6_RECVPKTINFO
154 if (from
->sa_family
== AF_INET6
)
155 setsockopt(s
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &on
, sizeof(on
));
158 bzero(&msg
, sizeof msg
); /* Clear possible system-dependent fields */
159 msg
.msg_control
= control_un
.control
;
160 msg
.msg_controllen
= sizeof(control_un
);
164 msg
.msg_namelen
= *fromlen
;
170 if ((n
= recvmsg(s
, &msg
, flags
)) < 0)
171 return n
; /* Error */
173 *fromlen
= msg
.msg_namelen
;
176 bzero(myaddr
, sizeof(*myaddr
));
177 myaddr
->sa
.sa_family
= from
->sa_family
;
179 if (msg
.msg_controllen
< sizeof(struct cmsghdr
) ||
180 (msg
.msg_flags
& MSG_CTRUNC
))
181 return n
; /* No information available */
183 for (cmptr
= CMSG_FIRSTHDR(&msg
); cmptr
!= NULL
;
184 cmptr
= CMSG_NXTHDR(&msg
, cmptr
)) {
186 if (from
->sa_family
== AF_INET
) {
187 myaddr
->sa
.sa_family
= AF_INET
;
188 #ifdef IP_RECVDSTADDR
189 if (cmptr
->cmsg_level
== IPPROTO_IP
&&
190 cmptr
->cmsg_type
== IP_RECVDSTADDR
) {
191 memcpy(&myaddr
->si
.sin_addr
, CMSG_DATA(cmptr
),
192 sizeof(struct in_addr
));
197 if (cmptr
->cmsg_level
== IPPROTO_IP
&&
198 cmptr
->cmsg_type
== IP_PKTINFO
) {
199 memcpy(&pktinfo
, CMSG_DATA(cmptr
),
200 sizeof(struct in_pktinfo
));
201 memcpy(&myaddr
->si
.sin_addr
, &pktinfo
.ipi_addr
,
202 sizeof(struct in_addr
));
207 else if (from
->sa_family
== AF_INET6
) {
208 myaddr
->sa
.sa_family
= AF_INET6
;
209 #ifdef IP6_RECVDSTADDR
210 if (cmptr
->cmsg_level
== IPPROTO_IPV6
&&
211 cmptr
->cmsg_type
== IPV6_RECVDSTADDR
)
212 memcpy(&myaddr
->s6
.sin6_addr
, CMSG_DATA(cmptr
),
213 sizeof(struct in6_addr
));
216 #ifdef HAVE_STRUCT_IN6_PKTINFO
217 if (cmptr
->cmsg_level
== IPPROTO_IPV6
&&
218 (cmptr
->cmsg_type
== IPV6_RECVPKTINFO
||
219 cmptr
->cmsg_type
== IPV6_PKTINFO
)) {
220 memcpy(&pktinfo6
, CMSG_DATA(cmptr
),
221 sizeof(struct in6_pktinfo
));
222 memcpy(&myaddr
->s6
.sin6_addr
, &pktinfo6
.ipi6_addr
,
223 sizeof(struct in6_addr
));
229 /* If the address is not a valid local address,
230 * then bind to any address...
232 if (address_is_local(myaddr
) != 1) {
233 if (myaddr
->sa
.sa_family
== AF_INET
)
234 ((struct sockaddr_in
*)myaddr
)->sin_addr
.s_addr
= INADDR_ANY
;
236 else if (myaddr
->sa
.sa_family
== AF_INET6
)
237 memset(&myaddr
->s6
.sin6_addr
, 0, sizeof(struct in6_addr
));
244 #else /* pointless... */
247 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
248 struct sockaddr
*from
, int *fromlen
, union sock_addr
*myaddr
)
250 /* There is no way we can get the local address, fudge it */
252 bzero(myaddr
, sizeof(*myaddr
));
253 myaddr
->sa
.sa_family
= from
->sa_family
;
254 sa_set_port(myaddr
, htons(IPPORT_TFTP
));
256 return recvfrom(s
, buf
, len
, flags
, from
, fromlen
);