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! */
22 #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 struct sockaddr_in
*addr
)
60 struct sockaddr_in sin
;
66 /* Multicast or universal broadcast address? */
67 if (ntohl(addr
->sin_addr
.s_addr
) >= (224UL << 24))
70 sockfd
= socket(PF_INET
, SOCK_DGRAM
, 0);
74 if (connect(sockfd
, (const struct sockaddr
*)addr
, sizeof *addr
))
78 if (getsockname(sockfd
, (struct sockaddr
*)&sin
, &addrlen
))
81 rv
= sin
.sin_addr
.s_addr
== addr
->sin_addr
.s_addr
;
95 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
96 struct sockaddr
*from
, socklen_t
*fromlen
,
97 struct sockaddr_in
*myaddr
)
102 struct cmsghdr
*cmptr
;
106 char control
[CMSG_SPACE(sizeof(struct in_addr
)) +
107 CMSG_SPACE(sizeof(struct in_pktinfo
))];
109 char control
[CMSG_SPACE(sizeof(struct in_addr
))];
114 struct in_pktinfo pktinfo
;
117 /* Try to enable getting the return address */
118 #ifdef IP_RECVDSTADDR
119 setsockopt(s
, IPPROTO_IP
, IP_RECVDSTADDR
, &on
, sizeof(on
));
122 setsockopt(s
, IPPROTO_IP
, IP_PKTINFO
, &on
, sizeof(on
));
125 bzero(&msg
, sizeof msg
); /* Clear possible system-dependent fields */
126 msg
.msg_control
= control_un
.control
;
127 msg
.msg_controllen
= sizeof(control_un
.control
);
131 msg
.msg_namelen
= *fromlen
;
137 if ( (n
= recvmsg(s
, &msg
, flags
)) < 0 )
138 return n
; /* Error */
140 *fromlen
= msg
.msg_namelen
;
143 bzero(myaddr
, sizeof(struct sockaddr_in
));
144 myaddr
->sin_family
= AF_INET
;
146 if ( msg
.msg_controllen
< sizeof(struct cmsghdr
) ||
147 (msg
.msg_flags
& MSG_CTRUNC
) )
148 return n
; /* No information available */
150 for ( cmptr
= CMSG_FIRSTHDR(&msg
) ; cmptr
!= NULL
;
151 cmptr
= CMSG_NXTHDR(&msg
, cmptr
) ) {
153 #ifdef IP_RECVDSTADDR
154 if ( cmptr
->cmsg_level
== IPPROTO_IP
&&
155 cmptr
->cmsg_type
== IP_RECVDSTADDR
) {
156 memcpy(&myaddr
->sin_addr
, CMSG_DATA(cmptr
),
157 sizeof(struct in_addr
));
162 if ( cmptr
->cmsg_level
== IPPROTO_IP
&&
163 cmptr
->cmsg_type
== IP_PKTINFO
) {
164 memcpy(&pktinfo
, CMSG_DATA(cmptr
), sizeof(struct in_pktinfo
));
165 memcpy(&myaddr
->sin_addr
, &pktinfo
.ipi_addr
, sizeof(struct in_addr
));
172 /* If the address is not a valid local address, then bind to any address... */
173 if (address_is_local(myaddr
) != 1)
174 myaddr
->sin_addr
.s_addr
= INADDR_ANY
;
179 #else /* pointless... */
182 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
183 struct sockaddr
*from
, int *fromlen
,
184 struct sockaddr_in
*myaddr
)
186 /* There is no way we can get the local address, fudge it */
188 bzero(myaddr
, sizeof(struct sockaddr_in
));
189 myaddr
->sin_family
= AF_INET
;
191 myaddr
->sin_port
= htons(IPPORT_TFTP
);
192 bzero(&myaddr
->sin_addr
, sizeof(myaddr
->sin_addr
));
194 return recvfrom(s
,buf
,len
,flags
,from
,fromlen
);