Massively cleaned up #include's, so they're in a consistent
[freeradius.git] / src / lib / udpfromto.c
1 /*
2  *   This library is free software; you can redistribute it and/or
3  *   modify it under the terms of the GNU Lesser General Public
4  *   License as published by the Free Software Foundation; either
5  *   version 2.1 of the License, or (at your option) any later version.
6  *
7  *   This library is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10  *   Lesser General Public License for more details.
11  *
12  *   You should have received a copy of the GNU Lesser General Public
13  *   License along with this library; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  *
16  *  Helper functions to get/set addresses of UDP packets
17  *  based on recvfromto by Miquel van Smoorenburg
18  *
19  * recvfromto   Like recvfrom, but also stores the destination
20  *              IP address. Useful on multihomed hosts.
21  *
22  *              Should work on Linux and BSD.
23  *
24  *              Copyright (C) 2002 Miquel van Smoorenburg.
25  *
26  *              This program is free software; you can redistribute it and/or
27  *              modify it under the terms of the GNU Lesser General Public
28  *              License as published by the Free Software Foundation; either
29  *              version 2 of the License, or (at your option) any later version.
30  *
31  * sendfromto   added 18/08/2003, Jan Berkel <jan@sitadelle.com>
32  *              Works on Linux and FreeBSD (5.x)
33  *
34  * Version: $Id$
35  */
36
37 #include <freeradius-devel/ident.h>
38 RCSID("$Id$")
39
40 #include <freeradius-devel/udpfromto.h>
41
42 #ifdef WITH_UDPFROMTO
43
44 #ifdef HAVE_SYS_UIO_H
45 #include <sys/uio.h>
46 #endif
47
48 #include <fcntl.h>
49
50
51 int udpfromto_init(int s)
52 {
53         int err = -1, opt = 1;
54         errno = ENOSYS;
55 #ifdef HAVE_IP_PKTINFO
56         /* Set the IP_PKTINFO option (Linux). */
57         err = setsockopt(s, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
58 #endif
59
60 #ifdef HAVE_IP_RECVDSTADDR
61         /*
62          * Set the IP_RECVDSTADDR option (BSD).
63          * Note: IP_RECVDSTADDR == IP_SENDSRCADDR
64          */
65         err = setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));
66 #endif
67         return err;
68 }
69
70 int recvfromto(int s, void *buf, size_t len, int flags,
71         struct sockaddr *from, socklen_t *fromlen,
72         struct sockaddr *to, socklen_t *tolen)
73 {
74 #if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR)
75         struct msghdr msgh;
76         struct cmsghdr *cmsg;
77         struct iovec iov;
78         char cbuf[256];
79         int err;
80
81         /*
82          *      If from or to are set, they must be big enough
83          *      to store a struct sockaddr_in.
84          */
85         if ((from && (!fromlen || *fromlen < sizeof(struct sockaddr_in))) ||
86             (to   && (!tolen   || *tolen   < sizeof(struct sockaddr_in)))) {
87                 errno = EINVAL;
88                 return -1;
89         }
90
91         /*
92          *      IP_PKTINFO / IP_RECVDSTADDR don't provide sin_port so we have to
93          *      retrieve it using getsockname().
94          */
95         if (to) {
96                 struct sockaddr_in si;
97                 socklen_t l = sizeof(si);
98
99                 ((struct sockaddr_in *)to)->sin_family = AF_INET;
100                 ((struct sockaddr_in *)to)->sin_port = 0;
101                 l = sizeof(si);
102                 if (getsockname(s, (struct sockaddr *)&si, &l) == 0) {
103                         ((struct sockaddr_in *)to)->sin_port = si.sin_port;
104                         ((struct sockaddr_in *)to)->sin_addr = si.sin_addr;
105                 }
106                 if (tolen) *tolen = sizeof(struct sockaddr_in);
107         }
108
109         /* Set up iov and msgh structures. */
110         memset(&msgh, 0, sizeof(struct msghdr));
111         iov.iov_base = buf;
112         iov.iov_len  = len;
113         msgh.msg_control = cbuf;
114         msgh.msg_controllen = sizeof(cbuf);
115         msgh.msg_name = from;
116         msgh.msg_namelen = fromlen ? *fromlen : 0;
117         msgh.msg_iov  = &iov;
118         msgh.msg_iovlen = 1;
119         msgh.msg_flags = 0;
120
121         /* Receive one packet. */
122         if ((err = recvmsg(s, &msgh, flags)) < 0) {
123                 return err;
124         }
125         if (fromlen) *fromlen = msgh.msg_namelen;
126
127         /* Process auxiliary received data in msgh */
128         for (cmsg = CMSG_FIRSTHDR(&msgh);
129              cmsg != NULL;
130              cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
131
132 # ifdef HAVE_IP_PKTINFO
133                 if (cmsg->cmsg_level == SOL_IP
134                     && cmsg->cmsg_type == IP_PKTINFO) {
135                         struct in_pktinfo *i =
136                                 (struct in_pktinfo *)CMSG_DATA(cmsg);
137                         if (to) {
138                                 ((struct sockaddr_in *)to)->sin_addr =
139                                         i->ipi_addr;
140                                 if (tolen) *tolen = sizeof(struct sockaddr_in);
141                         }
142                         break;
143                 }
144 # endif
145
146 # ifdef HAVE_IP_RECVDSTADDR
147                 if (cmsg->cmsg_level == IPPROTO_IP
148                     && cmsg->cmsg_type == IP_RECVDSTADDR) {
149                         struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
150                         if (to) {
151                                 ((struct sockaddr_in *)to)->sin_addr = *i;
152                                 if (tolen) *tolen = sizeof(struct sockaddr_in);
153                         }
154                         break;
155                 }
156 # endif
157         }
158         return err;
159 #else
160         /* fallback: call recvfrom */
161         return recvfrom(s, buf, len, flags, from, fromlen);
162 #endif /* defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR) */
163 }
164
165 int sendfromto(int s, void *buf, size_t len, int flags,
166                           struct sockaddr *from, socklen_t fromlen,
167                           struct sockaddr *to, socklen_t tolen)
168 {
169 #if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_SENDSRCADDR)
170         struct msghdr msgh;
171         struct cmsghdr *cmsg;
172         struct iovec iov;
173 # ifdef HAVE_IP_PKTINFO
174         char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
175         struct in_pktinfo pktinfo, *pktinfo_ptr;
176         memset(&pktinfo, 0, sizeof(struct in_pktinfo));
177 # endif
178         struct sockaddr_in      *s4;
179
180 # ifdef HAVE_IP_SENDSRCADDR
181         char cmsgbuf[CMSG_SPACE(sizeof(struct in_addr))];
182 # endif
183
184         s4 = (struct sockaddr_in *) from;
185
186         /*
187          *      Source is unset, empty, or unspecified,
188          *      fall back to sendto().
189          */
190         if (!s4 || (fromlen == 0) || (s4->sin_family == AF_UNSPEC)) {
191                 return sendto(s, buf, len, flags, to, tolen);
192         }
193
194         /* Set up iov and msgh structures. */
195         memset(&msgh, 0, sizeof(struct msghdr));
196         iov.iov_base = buf;
197         iov.iov_len = len;
198         msgh.msg_iov = &iov;
199         msgh.msg_iovlen = 1;
200         msgh.msg_control = cmsgbuf;
201         msgh.msg_controllen = sizeof(cmsgbuf);
202         msgh.msg_name = to;
203         msgh.msg_namelen = tolen;
204
205         cmsg = CMSG_FIRSTHDR(&msgh);
206
207 # ifdef HAVE_IP_PKTINFO
208         cmsg->cmsg_level = SOL_IP;
209         cmsg->cmsg_type = IP_PKTINFO;
210         cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
211         pktinfo.ipi_spec_dst = ((struct sockaddr_in *)from)->sin_addr;
212         pktinfo_ptr = (struct in_pktinfo *)CMSG_DATA(cmsg);
213         memcpy(pktinfo_ptr, &pktinfo, sizeof(struct in_pktinfo));
214 # endif
215 # ifdef HAVE_IP_SENDSRCADDR
216         cmsg->cmsg_level = IPPROTO_IP;
217         cmsg->cmsg_type = IP_SENDSRCADDR;
218         cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
219         memcpy((struct in_addr *)CMSG_DATA(cmsg),
220                &((struct sockaddr_in *)from)->sin_addr, sizeof(struct in_addr));
221 # endif
222
223         return sendmsg(s, &msgh, flags);
224 #else
225         /* fallback: call sendto() */
226         return sendto(s, buf, len, flags, to, tolen);
227 #endif  /* defined(HAVE_IP_PKTINFO) || defined (HAVE_IP_SENDSRCADDR) */
228 }
229
230
231 #ifdef TESTING
232 /*
233  *      Small test program to test recvfromto/sendfromto
234  *
235  *      use a virtual IP address as first argument to test
236  *
237  *      reply packet should originate from virtual IP and not
238  *      from the default interface the alias is bound to
239  */
240
241 #include <stdio.h>
242 #include <stdlib.h>
243 #include <arpa/inet.h>
244 #include <sys/types.h>
245 #include <sys/wait.h>
246
247 #define DEF_PORT 20000          /* default port to listen on */
248 #define DESTIP "127.0.0.1"      /* send packet to localhost per default */
249 #define TESTSTRING "foo"        /* what to send */
250 #define TESTLEN 4                       /* 4 bytes */
251
252 int main(int argc, char **argv)
253 {
254         struct sockaddr_in from, to, in;
255         char buf[TESTLEN];
256         char *destip = DESTIP;
257         int port = DEF_PORT;
258         int n, server_socket, client_socket, fl, tl, pid;
259
260         if (argc > 1) destip = argv[1];
261         if (argc > 2) port = atoi(argv[2]);
262
263         in.sin_family = AF_INET;
264         in.sin_addr.s_addr = INADDR_ANY;
265         in.sin_port = htons(port);
266         fl = tl = sizeof(struct sockaddr_in);
267         memset(&from, 0, sizeof(from));
268         memset(&to,   0, sizeof(to));
269
270         switch(pid = fork()) {
271                 case -1:
272                         perror("fork");
273                         return 0;
274                 case 0:
275                         /* child */
276                         usleep(100000);
277                         goto client;
278         }
279
280         /* parent: server */
281         server_socket = socket(PF_INET, SOCK_DGRAM, 0);
282         if (udpfromto_init(server_socket) != 0) {
283                 perror("udpfromto_init\n");
284                 waitpid(pid, NULL, WNOHANG);
285                 return 0;
286         }
287
288         if (bind(server_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
289                 perror("server: bind");
290                 waitpid(pid, NULL, WNOHANG);
291                 return 0;
292         }
293
294         printf("server: waiting for packets on INADDR_ANY:%d\n", port);
295         if ((n = recvfromto(server_socket, buf, sizeof(buf), 0,
296             (struct sockaddr *)&from, &fl,
297             (struct sockaddr *)&to, &tl)) < 0) {
298                 perror("server: recvfromto");
299                 waitpid(pid, NULL, WNOHANG);
300                 return 0;
301         }
302
303         printf("server: received a packet of %d bytes [%s] ", n, buf);
304         printf("(src ip:port %s:%d ",
305                 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
306         printf(" dst ip:port %s:%d)\n",
307                 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
308
309         printf("server: replying from address packet was received on to source address\n");
310
311         if ((n = sendfromto(server_socket, buf, n, 0,
312                 (struct sockaddr *)&to, tl,
313                 (struct sockaddr *)&from, fl)) < 0) {
314                 perror("server: sendfromto");
315         }
316
317         waitpid(pid, NULL, 0);
318         return 0;
319
320 client:
321         close(server_socket);
322         client_socket = socket(PF_INET, SOCK_DGRAM, 0);
323         if (udpfromto_init(client_socket) != 0) {
324                 perror("udpfromto_init");
325                 _exit(0);
326         }
327         /* bind client on different port */
328         in.sin_port = htons(port+1);
329         if (bind(client_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
330                 perror("client: bind");
331                 _exit(0);
332         }
333
334         in.sin_port = htons(port);
335         in.sin_addr.s_addr = inet_addr(destip);
336
337         printf("client: sending packet to %s:%d\n", destip, port);
338         if (sendto(client_socket, TESTSTRING, TESTLEN, 0,
339                         (struct sockaddr *)&in, sizeof(in)) < 0) {
340                 perror("client: sendto");
341                 _exit(0);
342         }
343
344         printf("client: waiting for reply from server on INADDR_ANY:%d\n", port+1);
345
346         if ((n = recvfromto(client_socket, buf, sizeof(buf), 0,
347             (struct sockaddr *)&from, &fl,
348             (struct sockaddr *)&to, &tl)) < 0) {
349                 perror("client: recvfromto");
350                 _exit(0);
351         }
352
353         printf("client: received a packet of %d bytes [%s] ", n, buf);
354         printf("(src ip:port %s:%d",
355                 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
356         printf(" dst ip:port %s:%d)\n",
357                 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
358
359         _exit(0);
360 }
361
362 #endif /* TESTING */
363 #endif /* WITH_UDPFROMTO */