1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2001-2006 H. Peter Anvin - All Rights Reserved
4  *
5  *   This program is free software available under the same license
6  *   as the "OpenBSD" operating system, distributed at
7  *   http://www.openbsd.org/.
8  *
9  * ----------------------------------------------------------------------- */
10 
11 /*
12  * recvfrom.c
13  *
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.
17  *
18  */
19 
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
26 
27 #if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL)
28 
29 #include <sys/uio.h>
30 
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;
39 };
40 #  else
41 #   undef IP_PKTINFO            /* No definition, no way to get it */
42 #  endif
43 # endif
44 #endif
45 
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
52 
53 /*
54  * Check to see if this is a valid local address, meaning that we can
55  * legally bind to it.
56  */
address_is_local(const union sock_addr * addr)57 static int address_is_local(const union sock_addr *addr)
58 {
59     union sock_addr sa1, sa2;
60     int sockfd = -1;
61     int e;
62     int rv = 0;
63     socklen_t addrlen;
64 
65     memcpy(&sa1, addr, sizeof sa1);
66 
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 */
72     }
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 */
78     }
79 #endif
80     else
81         return 0;
82 
83     sockfd = socket(sa1.sa.sa_family, SOCK_DGRAM, 0);
84     if (sockfd < 0)
85         goto err;
86 
87     if (bind(sockfd, &sa1.sa, SOCKLEN(&sa1)))
88         goto err;
89 
90     addrlen = SOCKLEN(addr);
91     if (getsockname(sockfd, (struct sockaddr *)&sa2, &addrlen))
92         goto err;
93 
94     if (sa1.sa.sa_family != sa2.sa.sa_family)
95 	goto err;
96 
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;
105 
106 err:
107     e = errno;
108 
109     if (sockfd >= 0)
110         close(sockfd);
111 
112     errno = e;
113     return rv;
114 }
115 
116 int
myrecvfrom(int s,void * buf,int len,unsigned int flags,struct sockaddr * from,socklen_t * fromlen,union sock_addr * myaddr)117 myrecvfrom(int s, void *buf, int len, unsigned int flags,
118            struct sockaddr *from, socklen_t * fromlen,
119            union sock_addr *myaddr)
120 {
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
149 
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;
169 
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;
176 
177     if ((n = recvmsg(s, &msg, flags)) < 0)
178         return n;               /* Error */
179 
180     *fromlen = msg.msg_namelen;
181 
182     if (myaddr) {
183         bzero(myaddr, sizeof(*myaddr));
184         myaddr->sa.sa_family = from->sa_family;
185 
186         if (msg.msg_controllen < sizeof(struct cmsghdr) ||
187             (msg.msg_flags & MSG_CTRUNC))
188             return n;           /* No information available */
189 
190         for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
191              cmptr = CMSG_NXTHDR(&msg, cmptr)) {
192 
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));
200                 }
201 #endif
202 
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));
210                 }
211 #endif
212             }
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
222 
223 #ifdef HAVE_STRUCT_IN6_PKTINFO
224                 if (cmptr->cmsg_level == IPPROTO_IPV6 &&
225                     (cmptr->cmsg_type == IPV6_RECVPKTINFO ||
226                      cmptr->cmsg_type == IPV6_PKTINFO)) {
227                     memcpy(&pktinfo6, CMSG_DATA(cmptr),
228                            sizeof(struct in6_pktinfo));
229                     memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr,
230                            sizeof(struct in6_addr));
231                 }
232 #endif
233             }
234 #endif
235         }
236         /* If the address is not a valid local address,
237          * then bind to any address...
238          */
239         if (address_is_local(myaddr) != 1) {
240             if (myaddr->sa.sa_family == AF_INET)
241                 ((struct sockaddr_in *)myaddr)->sin_addr.s_addr = INADDR_ANY;
242 #ifdef HAVE_IPV6
243             else if (myaddr->sa.sa_family == AF_INET6)
244                 memset(&myaddr->s6.sin6_addr, 0, sizeof(struct in6_addr));
245 #endif
246         }
247     }
248     return n;
249 }
250 
251 #else                           /* pointless... */
252 
253 int
myrecvfrom(int s,void * buf,int len,unsigned int flags,struct sockaddr * from,socklen_t * fromlen,union sock_addr * myaddr)254 myrecvfrom(int s, void *buf, int len, unsigned int flags,
255            struct sockaddr *from, socklen_t * fromlen,
256            union sock_addr *myaddr)
257 {
258     /* There is no way we can get the local address, fudge it */
259 
260     bzero(myaddr, sizeof(*myaddr));
261     myaddr->sa.sa_family = from->sa_family;
262     sa_set_port(myaddr, htons(IPPORT_TFTP));
263 
264     return recvfrom(s, buf, len, flags, from, fromlen);
265 }
266 
267 #endif
268