Use replacement library functions to daemonize, rather than #ifdef hell
[tftp-hpa.git] / tftpd / recvfrom.c
blobba8d648c69cd11a7d509f916b56190e27147758e
1 /* ----------------------------------------------------------------------- *
2 *
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 "recvfrom.h"
22 #include "common/tftpsubs.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 struct sockaddr_in *addr)
60 struct sockaddr_in sin;
61 int sockfd = -1;
62 int e;
63 int rv = 0;
64 socklen_t addrlen;
66 /* Multicast or universal broadcast address? */
67 if (ntohl(addr->sin_addr.s_addr) >= (224UL << 24))
68 return 0;
70 sockfd = socket(PF_INET, SOCK_DGRAM, 0);
71 if (sockfd < 0)
72 goto err;
74 if (connect(sockfd, (const struct sockaddr *)addr, sizeof *addr))
75 goto err;
77 addrlen = sizeof sin;
78 if (getsockname(sockfd, (struct sockaddr *)&sin, &addrlen))
79 goto err;
81 rv = sin.sin_addr.s_addr == addr->sin_addr.s_addr;
83 err:
84 e = errno;
86 if (sockfd >= 0)
87 close(sockfd);
89 errno = e;
90 return rv;
94 int
95 myrecvfrom(int s, void *buf, int len, unsigned int flags,
96 struct sockaddr *from, socklen_t *fromlen,
97 struct sockaddr_in *myaddr)
99 struct msghdr msg;
100 struct iovec iov;
101 int n;
102 struct cmsghdr *cmptr;
103 union {
104 struct cmsghdr cm;
105 #ifdef IP_PKTINFO
106 char control[CMSG_SPACE(sizeof(struct in_addr)) +
107 CMSG_SPACE(sizeof(struct in_pktinfo))];
108 #else
109 char control[CMSG_SPACE(sizeof(struct in_addr))];
110 #endif
111 } control_un;
112 int on = 1;
113 #ifdef IP_PKTINFO
114 struct in_pktinfo pktinfo;
115 #endif
117 /* Try to enable getting the return address */
118 #ifdef IP_RECVDSTADDR
119 setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
120 #endif
121 #ifdef IP_PKTINFO
122 setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
123 #endif
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);
128 msg.msg_flags = 0;
130 msg.msg_name = from;
131 msg.msg_namelen = *fromlen;
132 iov.iov_base = buf;
133 iov.iov_len = len;
134 msg.msg_iov = &iov;
135 msg.msg_iovlen = 1;
137 if ( (n = recvmsg(s, &msg, flags)) < 0 )
138 return n; /* Error */
140 *fromlen = msg.msg_namelen;
142 if ( myaddr ) {
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));
159 #endif
161 #ifdef IP_PKTINFO
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));
167 #endif
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;
176 return n;
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);
197 #endif