Update CHANGES for 5.0 release
[tftp-hpa.git] / tftpd / recvfrom.c
blob389ba82e5d3b93c40058fadc00dfd135a3d95cc4
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 * ----------------------------------------------------------------------- */
12 * recvfrom.c
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"
22 #include "recvfrom.h"
23 #ifdef HAVE_MACHINE_PARAM_H
24 #include <machine/param.h> /* Needed on some versions of FreeBSD */
25 #endif
27 #if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL)
29 #include <sys/uio.h>
31 #ifdef IP_PKTINFO
32 # ifndef HAVE_STRUCT_IN_PKTINFO
33 # ifdef __linux__
34 /* Assume this version of glibc simply lacks the definition */
35 struct in_pktinfo {
36 int ipi_ifindex;
37 struct in_addr ipi_spec_dst;
38 struct in_addr ipi_addr;
40 # else
41 # undef IP_PKTINFO /* No definition, no way to get it */
42 # endif
43 # endif
44 #endif
46 #ifndef CMSG_LEN
47 # define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
48 #endif
49 #ifndef CMSG_SPACE
50 # define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
51 #endif
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
56 * bind to it.
58 static int address_is_local(const union sock_addr *addr)
60 union sock_addr sa;
61 int sockfd = -1;
62 int e;
63 int rv = 0;
64 socklen_t addrlen;
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))
69 return 0;
71 #ifdef HAVE_IPV6
72 else if (addr->sa.sa_family == AF_INET6) {
73 if (IN6_IS_ADDR_MULTICAST(&addr->s6.sin6_addr))
74 return 0;
76 #endif
77 else
78 return 0;
80 sockfd = socket(addr->sa.sa_family, SOCK_DGRAM, 0);
81 if (sockfd < 0)
82 goto err;
84 if (connect(sockfd, &addr->sa, SOCKLEN(addr)))
85 goto err;
87 addrlen = SOCKLEN(addr);
88 if (getsockname(sockfd, (struct sockaddr *)&sa, &addrlen))
89 goto err;
91 if (addr->sa.sa_family == AF_INET)
92 rv = sa.si.sin_addr.s_addr == addr->si.sin_addr.s_addr;
93 #ifdef HAVE_IPV6
94 else if (addr->sa.sa_family == AF_INET6)
95 rv = IN6_ARE_ADDR_EQUAL(&sa.s6.sin6_addr, &addr->s6.sin6_addr);
96 #endif
97 else
98 rv = 0;
99 err:
100 e = errno;
102 if (sockfd >= 0)
103 close(sockfd);
105 errno = e;
106 return rv;
110 myrecvfrom(int s, void *buf, int len, unsigned int flags,
111 struct sockaddr *from, socklen_t * fromlen,
112 union sock_addr *myaddr)
114 struct msghdr msg;
115 struct iovec iov;
116 int n;
117 struct cmsghdr *cmptr;
118 union {
119 struct cmsghdr cm;
120 #ifdef IP_PKTINFO
121 char control[CMSG_SPACE(sizeof(struct in_addr)) +
122 CMSG_SPACE(sizeof(struct in_pktinfo))];
123 #else
124 char control[CMSG_SPACE(sizeof(struct in_addr))];
125 #endif
126 #ifdef HAVE_IPV6
127 #ifdef HAVE_STRUCT_IN6_PKTINFO
128 char control6[CMSG_SPACE(sizeof(struct in6_addr)) +
129 CMSG_SPACE(sizeof(struct in6_pktinfo))];
130 #else
131 char control6[CMSG_SPACE(sizeof(struct in6_addr))];
132 #endif
133 #endif
134 } control_un;
135 int on = 1;
136 #ifdef IP_PKTINFO
137 struct in_pktinfo pktinfo;
138 #endif
139 #ifdef HAVE_STRUCT_IN6_PKTINFO
140 struct in6_pktinfo pktinfo6;
141 #endif
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));
147 #endif
148 #ifdef IP_PKTINFO
149 if (from->sa_family == AF_INET)
150 setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
151 #endif
152 #ifdef HAVE_IPV6
153 #ifdef IPV6_RECVPKTINFO
154 if (from->sa_family == AF_INET6)
155 setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
156 #endif
157 #endif
158 bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */
159 msg.msg_control = control_un.control;
160 msg.msg_controllen = sizeof(control_un);
161 msg.msg_flags = 0;
163 msg.msg_name = from;
164 msg.msg_namelen = *fromlen;
165 iov.iov_base = buf;
166 iov.iov_len = len;
167 msg.msg_iov = &iov;
168 msg.msg_iovlen = 1;
170 if ((n = recvmsg(s, &msg, flags)) < 0)
171 return n; /* Error */
173 *fromlen = msg.msg_namelen;
175 if (myaddr) {
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));
194 #endif
196 #ifdef IP_PKTINFO
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));
204 #endif
206 #ifdef HAVE_IPV6
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));
214 #endif
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));
225 #endif
227 #endif
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;
235 #ifdef HAVE_IPV6
236 else if (myaddr->sa.sa_family == AF_INET6)
237 memset(&myaddr->s6.sin6_addr, 0, sizeof(struct in6_addr));
238 #endif
241 return n;
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);
259 #endif