Pull fix from branch_1_1, so proxied EAP replies work
[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 #ifdef WITH_UDPFROMTO
38
39 #include <freeradius-devel/ident.h>
40 RCSID("$Id$")
41
42 #include <freeradius-devel/autoconf.h>
43
44 #include <sys/types.h>
45
46 #ifdef HAVE_SYS_UIO_H
47 #include <sys/uio.h>
48 #endif
49
50 #include <netinet/in.h>
51 #include <errno.h>
52 #include <unistd.h>
53 #include <fcntl.h>
54 #include <string.h>
55
56 #include <freeradius-devel/udpfromto.h>
57
58 int udpfromto_init(int s)
59 {
60         int err = -1, opt = 1;
61         errno = ENOSYS;
62 #ifdef HAVE_IP_PKTINFO
63         /* Set the IP_PKTINFO option (Linux). */
64         err = setsockopt(s, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
65 #endif
66
67 #ifdef HAVE_IP_RECVDSTADDR
68         /*
69          * Set the IP_RECVDSTADDR option (BSD).
70          * Note: IP_RECVDSTADDR == IP_SENDSRCADDR
71          */
72         err = setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));
73 #endif
74         return err;
75 }
76
77 int recvfromto(int s, void *buf, size_t len, int flags,
78         struct sockaddr *from, socklen_t *fromlen,
79         struct sockaddr *to, socklen_t *tolen)
80 {
81 #if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR)
82         struct msghdr msgh;
83         struct cmsghdr *cmsg;
84         struct iovec iov;
85         char cbuf[256];
86         int err;
87
88         /*
89          *      If from or to are set, they must be big enough
90          *      to store a struct sockaddr_in.
91          */
92         if ((from && (!fromlen || *fromlen < sizeof(struct sockaddr_in))) ||
93             (to   && (!tolen   || *tolen   < sizeof(struct sockaddr_in)))) {
94                 errno = EINVAL;
95                 return -1;
96         }
97
98         /*
99          *      IP_PKTINFO / IP_RECVDSTADDR don't provide sin_port so we have to
100          *      retrieve it using getsockname().
101          */
102         if (to) {
103                 struct sockaddr_in si;
104                 socklen_t l = sizeof(si);
105
106                 ((struct sockaddr_in *)to)->sin_family = AF_INET;
107                 ((struct sockaddr_in *)to)->sin_port = 0;
108                 l = sizeof(si);
109                 if (getsockname(s, (struct sockaddr *)&si, &l) == 0) {
110                         ((struct sockaddr_in *)to)->sin_port = si.sin_port;
111                         ((struct sockaddr_in *)to)->sin_addr = si.sin_addr;
112                 }
113                 if (tolen) *tolen = sizeof(struct sockaddr_in);
114         }
115
116         /* Set up iov and msgh structures. */
117         memset(&msgh, 0, sizeof(struct msghdr));
118         iov.iov_base = buf;
119         iov.iov_len  = len;
120         msgh.msg_control = cbuf;
121         msgh.msg_controllen = sizeof(cbuf);
122         msgh.msg_name = from;
123         msgh.msg_namelen = fromlen ? *fromlen : 0;
124         msgh.msg_iov  = &iov;
125         msgh.msg_iovlen = 1;
126         msgh.msg_flags = 0;
127
128         /* Receive one packet. */
129         if ((err = recvmsg(s, &msgh, flags)) < 0) {
130                 return err;
131         }
132         if (fromlen) *fromlen = msgh.msg_namelen;
133
134         /* Process auxiliary received data in msgh */
135         for (cmsg = CMSG_FIRSTHDR(&msgh);
136              cmsg != NULL;
137              cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
138
139 # ifdef HAVE_IP_PKTINFO
140                 if (cmsg->cmsg_level == SOL_IP
141                     && cmsg->cmsg_type == IP_PKTINFO) {
142                         struct in_pktinfo *i =
143                                 (struct in_pktinfo *)CMSG_DATA(cmsg);
144                         if (to) {
145                                 ((struct sockaddr_in *)to)->sin_addr =
146                                         i->ipi_addr;
147                                 if (tolen) *tolen = sizeof(struct sockaddr_in);
148                         }
149                         break;
150                 }
151 # endif
152
153 # ifdef HAVE_IP_RECVDSTADDR
154                 if (cmsg->cmsg_level == IPPROTO_IP
155                     && cmsg->cmsg_type == IP_RECVDSTADDR) {
156                         struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
157                         if (to) {
158                                 ((struct sockaddr_in *)to)->sin_addr = *i;
159                                 if (tolen) *tolen = sizeof(struct sockaddr_in);
160                         }
161                         break;
162                 }
163 # endif
164         }
165         return err;
166 #else
167         /* fallback: call recvfrom */
168         return recvfrom(s, buf, len, flags, from, fromlen);
169 #endif /* defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR) */
170 }
171
172 int sendfromto(int s, void *buf, size_t len, int flags,
173                           struct sockaddr *from, socklen_t fromlen,
174                           struct sockaddr *to, socklen_t tolen)
175 {
176 #if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_SENDSRCADDR)
177         struct msghdr msgh;
178         struct cmsghdr *cmsg;
179         struct iovec iov;
180 # ifdef HAVE_IP_PKTINFO
181         char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
182         struct in_pktinfo pktinfo, *pktinfo_ptr;
183         memset(&pktinfo, 0, sizeof(struct in_pktinfo));
184 # endif
185         struct sockaddr_in      *s4;
186
187 # ifdef HAVE_IP_SENDSRCADDR
188         char cmsgbuf[CMSG_SPACE(sizeof(struct in_addr))];
189 # endif
190
191         s4 = (struct sockaddr_in *) from;
192
193         /*
194          *      Source is unset, empty, or unspecified,
195          *      fall back to sendto().
196          */
197         if (!s4 || (fromlen == 0) || (s4->sin_family == AF_UNSPEC)) {
198                 return sendto(s, buf, len, flags, to, tolen);
199         }
200
201         /* Set up iov and msgh structures. */
202         memset(&msgh, 0, sizeof(struct msghdr));
203         iov.iov_base = buf;
204         iov.iov_len = len;
205         msgh.msg_iov = &iov;
206         msgh.msg_iovlen = 1;
207         msgh.msg_control = cmsgbuf;
208         msgh.msg_controllen = sizeof(cmsgbuf);
209         msgh.msg_name = to;
210         msgh.msg_namelen = tolen;
211
212         cmsg = CMSG_FIRSTHDR(&msgh);
213
214 # ifdef HAVE_IP_PKTINFO
215         cmsg->cmsg_level = SOL_IP;
216         cmsg->cmsg_type = IP_PKTINFO;
217         cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
218         pktinfo.ipi_spec_dst = ((struct sockaddr_in *)from)->sin_addr;
219         pktinfo_ptr = (struct in_pktinfo *)CMSG_DATA(cmsg);
220         memcpy(pktinfo_ptr, &pktinfo, sizeof(struct in_pktinfo));
221 # endif
222 # ifdef HAVE_IP_SENDSRCADDR
223         cmsg->cmsg_level = IPPROTO_IP;
224         cmsg->cmsg_type = IP_SENDSRCADDR;
225         cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
226         memcpy((struct in_addr *)CMSG_DATA(cmsg),
227                &((struct sockaddr_in *)from)->sin_addr, sizeof(struct in_addr));
228 # endif
229
230         return sendmsg(s, &msgh, flags);
231 #else
232         /* fallback: call sendto() */
233         return sendto(s, buf, len, flags, to, tolen);
234 #endif  /* defined(HAVE_IP_PKTINFO) || defined (HAVE_IP_SENDSRCADDR) */
235 }
236
237
238 #ifdef TESTING
239 /*
240  *      Small test program to test recvfromto/sendfromto
241  *
242  *      use a virtual IP address as first argument to test
243  *
244  *      reply packet should originate from virtual IP and not
245  *      from the default interface the alias is bound to
246  */
247
248 #include <stdio.h>
249 #include <stdlib.h>
250 #include <arpa/inet.h>
251 #include <sys/types.h>
252 #include <sys/wait.h>
253
254 #define DEF_PORT 20000          /* default port to listen on */
255 #define DESTIP "127.0.0.1"      /* send packet to localhost per default */
256 #define TESTSTRING "foo"        /* what to send */
257 #define TESTLEN 4                       /* 4 bytes */
258
259 int main(int argc, char **argv)
260 {
261         struct sockaddr_in from, to, in;
262         char buf[TESTLEN];
263         char *destip = DESTIP;
264         int port = DEF_PORT;
265         int n, server_socket, client_socket, fl, tl, pid;
266
267         if (argc > 1) destip = argv[1];
268         if (argc > 2) port = atoi(argv[2]);
269
270         in.sin_family = AF_INET;
271         in.sin_addr.s_addr = INADDR_ANY;
272         in.sin_port = htons(port);
273         fl = tl = sizeof(struct sockaddr_in);
274         memset(&from, 0, sizeof(from));
275         memset(&to,   0, sizeof(to));
276
277         switch(pid = fork()) {
278                 case -1:
279                         perror("fork");
280                         return 0;
281                 case 0:
282                         /* child */
283                         usleep(100000);
284                         goto client;
285         }
286
287         /* parent: server */
288         server_socket = socket(PF_INET, SOCK_DGRAM, 0);
289         if (udpfromto_init(server_socket) != 0) {
290                 perror("udpfromto_init\n");
291                 waitpid(pid, NULL, WNOHANG);
292                 return 0;
293         }
294
295         if (bind(server_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
296                 perror("server: bind");
297                 waitpid(pid, NULL, WNOHANG);
298                 return 0;
299         }
300
301         printf("server: waiting for packets on INADDR_ANY:%d\n", port);
302         if ((n = recvfromto(server_socket, buf, sizeof(buf), 0,
303             (struct sockaddr *)&from, &fl,
304             (struct sockaddr *)&to, &tl)) < 0) {
305                 perror("server: recvfromto");
306                 waitpid(pid, NULL, WNOHANG);
307                 return 0;
308         }
309
310         printf("server: received a packet of %d bytes [%s] ", n, buf);
311         printf("(src ip:port %s:%d ",
312                 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
313         printf(" dst ip:port %s:%d)\n",
314                 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
315
316         printf("server: replying from address packet was received on to source address\n");
317
318         if ((n = sendfromto(server_socket, buf, n, 0,
319                 (struct sockaddr *)&to, tl,
320                 (struct sockaddr *)&from, fl)) < 0) {
321                 perror("server: sendfromto");
322         }
323
324         waitpid(pid, NULL, 0);
325         return 0;
326
327 client:
328         close(server_socket);
329         client_socket = socket(PF_INET, SOCK_DGRAM, 0);
330         if (udpfromto_init(client_socket) != 0) {
331                 perror("udpfromto_init");
332                 _exit(0);
333         }
334         /* bind client on different port */
335         in.sin_port = htons(port+1);
336         if (bind(client_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
337                 perror("client: bind");
338                 _exit(0);
339         }
340
341         in.sin_port = htons(port);
342         in.sin_addr.s_addr = inet_addr(destip);
343
344         printf("client: sending packet to %s:%d\n", destip, port);
345         if (sendto(client_socket, TESTSTRING, TESTLEN, 0,
346                         (struct sockaddr *)&in, sizeof(in)) < 0) {
347                 perror("client: sendto");
348                 _exit(0);
349         }
350
351         printf("client: waiting for reply from server on INADDR_ANY:%d\n", port+1);
352
353         if ((n = recvfromto(client_socket, buf, sizeof(buf), 0,
354             (struct sockaddr *)&from, &fl,
355             (struct sockaddr *)&to, &tl)) < 0) {
356                 perror("client: recvfromto");
357                 _exit(0);
358         }
359
360         printf("client: received a packet of %d bytes [%s] ", n, buf);
361         printf("(src ip:port %s:%d",
362                 inet_ntoa(from.sin_addr), ntohs(from.sin_port));
363         printf(" dst ip:port %s:%d)\n",
364                 inet_ntoa(to.sin_addr), ntohs(to.sin_port));
365
366         _exit(0);
367 }
368
369 #endif /* TESTING */
370 #endif /* WITH_UDPFROMTO */