Support IPv6 on MacOS X systems
[tftp-hpa.git] / tftpd / recvfrom.c
blobd050b8075e5d422d0830adab2496cb204bba2933
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, meaning that we can
55 * legally bind to it.
57 static int address_is_local(const union sock_addr *addr)
59 union sock_addr sa1, sa2;
60 int sockfd = -1;
61 int e;
62 int rv = 0;
63 socklen_t addrlen;
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))
70 return 0;
71 sa1.si.sin_port = 0; /* Any port */
73 #ifdef HAVE_IPV6
74 else if (sa1.sa.sa_family == AF_INET6) {
75 if (IN6_IS_ADDR_MULTICAST(&sa1.s6.sin6_addr))
76 return 0;
77 sa1.s6.sin6_port = 0; /* Any port */
79 #endif
80 else
81 return 0;
83 sockfd = socket(sa1.sa.sa_family, SOCK_DGRAM, 0);
84 if (sockfd < 0)
85 goto err;
87 if (bind(sockfd, &sa1.sa, SOCKLEN(&sa1)))
88 goto err;
90 addrlen = SOCKLEN(addr);
91 if (getsockname(sockfd, (struct sockaddr *)&sa2, &addrlen))
92 goto err;
94 if (sa1.sa.sa_family != sa2.sa.sa_family)
95 goto err;
97 if (sa2.sa.sa_family == AF_INET)
98 rv = sa1.si.sin_addr.s_addr == sa2.si.sin_addr.s_addr;
99 #ifdef HAVE_IPV6
100 else if (sa2.sa.sa_family == AF_INET6)
101 rv = IN6_ARE_ADDR_EQUAL(&sa1.s6.sin6_addr, &sa2.s6.sin6_addr);
102 #endif
103 else
104 rv = 0;
106 err:
107 e = errno;
109 if (sockfd >= 0)
110 close(sockfd);
112 errno = e;
113 return rv;
117 myrecvfrom(int s, void *buf, int len, unsigned int flags,
118 struct sockaddr *from, socklen_t * fromlen,
119 union sock_addr *myaddr)
121 struct msghdr msg;
122 struct iovec iov;
123 int n;
124 struct cmsghdr *cmptr;
125 union {
126 struct cmsghdr cm;
127 #ifdef IP_PKTINFO
128 char control[CMSG_SPACE(sizeof(struct in_addr)) +
129 CMSG_SPACE(sizeof(struct in_pktinfo))];
130 #else
131 char control[CMSG_SPACE(sizeof(struct in_addr))];
132 #endif
133 #ifdef HAVE_IPV6
134 #ifdef HAVE_STRUCT_IN6_PKTINFO
135 char control6[CMSG_SPACE(sizeof(struct in6_addr)) +
136 CMSG_SPACE(sizeof(struct in6_pktinfo))];
137 #else
138 char control6[CMSG_SPACE(sizeof(struct in6_addr))];
139 #endif
140 #endif
141 } control_un;
142 int on = 1;
143 #ifdef IP_PKTINFO
144 struct in_pktinfo pktinfo;
145 #endif
146 #ifdef HAVE_STRUCT_IN6_PKTINFO
147 struct in6_pktinfo pktinfo6;
148 #endif
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));
154 #endif
155 #ifdef IP_PKTINFO
156 if (from->sa_family == AF_INET)
157 setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
158 #endif
159 #ifdef HAVE_IPV6
160 #ifdef IPV6_RECVPKTINFO
161 if (from->sa_family == AF_INET6)
162 setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
163 #endif
164 #endif
165 bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */
166 msg.msg_control = control_un.control;
167 msg.msg_controllen = sizeof(control_un);
168 msg.msg_flags = 0;
170 msg.msg_name = from;
171 msg.msg_namelen = *fromlen;
172 iov.iov_base = buf;
173 iov.iov_len = len;
174 msg.msg_iov = &iov;
175 msg.msg_iovlen = 1;
177 if ((n = recvmsg(s, &msg, flags)) < 0)
178 return n; /* Error */
180 *fromlen = msg.msg_namelen;
182 if (myaddr) {
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));
201 #endif
203 #ifdef IP_PKTINFO
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));
211 #endif
213 #ifdef HAVE_IPV6
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));
221 #endif
223 #ifdef HAVE_STRUCT_IN6_PKTINFO
224 if (cmptr->cmsg_level == IPPROTO_IPV6 &&
226 #ifdef IPV6_RECVPKTINFO
227 cmptr->cmsg_type == IPV6_RECVPKTINFO ||
228 #endif
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));
235 #endif
237 #endif
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;
245 #ifdef HAVE_IPV6
246 else if (myaddr->sa.sa_family == AF_INET6)
247 memset(&myaddr->s6.sin6_addr, 0, sizeof(struct in6_addr));
248 #endif
251 return n;
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);
270 #endif