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! */
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 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
55 struct sockaddr
*from
, int *fromlen
,
56 struct sockaddr_in
*myaddr
)
61 struct cmsghdr
*cmptr
;
65 char control
[CMSG_SPACE(sizeof(struct in_addr
)) +
66 CMSG_SPACE(sizeof(struct in_pktinfo
))];
68 char control
[CMSG_SPACE(sizeof(struct in_addr
))];
73 struct in_pktinfo pktinfo
;
76 /* Try to enable getting the return address */
78 setsockopt(s
, IPPROTO_IP
, IP_RECVDSTADDR
, &on
, sizeof(on
));
81 setsockopt(s
, IPPROTO_IP
, IP_PKTINFO
, &on
, sizeof(on
));
84 bzero(&msg
, sizeof msg
); /* Clear possible system-dependent fields */
85 msg
.msg_control
= control_un
.control
;
86 msg
.msg_controllen
= sizeof(control_un
.control
);
90 msg
.msg_namelen
= *fromlen
;
96 if ( (n
= recvmsg(s
, &msg
, flags
)) < 0 )
99 *fromlen
= msg
.msg_namelen
;
102 bzero(myaddr
, sizeof(struct sockaddr_in
));
103 myaddr
->sin_family
= AF_INET
;
105 if ( msg
.msg_controllen
< sizeof(struct cmsghdr
) ||
106 (msg
.msg_flags
& MSG_CTRUNC
) )
107 return n
; /* No information available */
109 for ( cmptr
= CMSG_FIRSTHDR(&msg
) ; cmptr
!= NULL
;
110 cmptr
= CMSG_NXTHDR(&msg
, cmptr
) ) {
112 #ifdef IP_RECVDSTADDR
113 if ( cmptr
->cmsg_level
== IPPROTO_IP
&&
114 cmptr
->cmsg_type
== IP_RECVDSTADDR
) {
115 memcpy(&myaddr
->sin_addr
, CMSG_DATA(cmptr
),
116 sizeof(struct in_addr
));
121 if ( cmptr
->cmsg_level
== IPPROTO_IP
&&
122 cmptr
->cmsg_type
== IP_PKTINFO
) {
123 memcpy(&pktinfo
, CMSG_DATA(cmptr
), sizeof(struct in_pktinfo
));
124 memcpy(&myaddr
->sin_addr
, &pktinfo
.ipi_addr
, sizeof(struct in_addr
));
134 #else /* pointless... */
137 myrecvfrom(int s
, void *buf
, int len
, unsigned int flags
,
138 struct sockaddr
*from
, int *fromlen
,
139 struct sockaddr_in
*myaddr
)
141 /* There is no way we can get the local address, fudge it */
143 bzero(myaddr
, sizeof(struct sockaddr_in
));
144 myaddr
->sin_family
= AF_INET
;
146 myaddr
->sin_port
= htons(IPPORT_TFTP
);
147 bzero(&myaddr
->sin_addr
, sizeof(myaddr
->sin_addr
));
149 return recvfrom(s
,buf
,len
,flags
,from
,fromlen
);