allowing build with only specific transports
[radsecproxy.git] / udp.c
1 /*
2  * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  */
8
9 #ifdef RADPROT_UDP
10 #include <signal.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <netdb.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <limits.h>
17 #ifdef SYS_SOLARIS9
18 #include <fcntl.h>
19 #endif
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <sys/select.h>
23 #include <ctype.h>
24 #include <sys/wait.h>
25 #include <arpa/inet.h>
26 #include <regex.h>
27 #include <pthread.h>
28 #include <openssl/ssl.h>
29 #include "debug.h"
30 #include "list.h"
31 #include "util.h"
32 #include "radsecproxy.h"
33
34 static void setprotoopts(struct commonprotoopts *opts);
35 static char **getlistenerargs();
36 void *udpserverrd(void *arg);
37 int clientradputudp(struct server *server, unsigned char *rad);
38 void addclientudp(struct client *client);
39 void addserverextraudp(struct clsrvconf *conf);
40 void udpsetsrcres();
41 void initextraudp();
42
43 static const struct protodefs protodefs = {
44     "udp",
45     NULL, /* secretdefault */
46     SOCK_DGRAM, /* socktype */
47     "1812", /* portdefault */
48     REQUEST_RETRY_COUNT, /* retrycountdefault */
49     10, /* retrycountmax */
50     REQUEST_RETRY_INTERVAL, /* retryintervaldefault */
51     60, /* retryintervalmax */
52     DUPLICATE_INTERVAL, /* duplicateintervaldefault */
53     setprotoopts, /* setprotoopts */
54     getlistenerargs, /* getlistenerargs */
55     udpserverrd, /* listener */
56     NULL, /* connecter */
57     NULL, /* clientconnreader */
58     clientradputudp, /* clientradput */
59     addclientudp, /* addclient */
60     addserverextraudp, /* addserverextra */
61     udpsetsrcres, /* setsrcres */
62     initextraudp /* initextra */
63 };
64
65 static int client4_sock = -1;
66 static int client6_sock = -1;
67 static struct queue *server_replyq = NULL;
68
69 static struct addrinfo *srcres = NULL;
70 static uint8_t handle;
71 static struct commonprotoopts *protoopts = NULL;
72
73 const struct protodefs *udpinit(uint8_t h) {
74     handle = h;
75     return &protodefs;
76 }
77
78 static void setprotoopts(struct commonprotoopts *opts) {
79     protoopts = opts;
80 }
81
82 static char **getlistenerargs() {
83     return protoopts ? protoopts->listenargs : NULL;
84 }
85
86 void udpsetsrcres() {
87     if (!srcres)
88         srcres = resolve_hostport_addrinfo(handle, protoopts ? protoopts->sourcearg : NULL);
89 }
90
91 void removeudpclientfromreplyq(struct client *c) {
92     struct list_node *n;
93     struct request *r;
94     
95     /* lock the common queue and remove replies for this client */
96     pthread_mutex_lock(&c->replyq->mutex);
97     for (n = list_first(c->replyq->entries); n; n = list_next(n)) {
98         r = (struct request *)n->data;
99         if (r->from == c)
100             r->from = NULL;
101     }
102     pthread_mutex_unlock(&c->replyq->mutex);
103 }       
104
105 /* exactly one of client and server must be non-NULL */
106 /* return who we received from in *client or *server */
107 /* return from in sa if not NULL */
108 unsigned char *radudpget(int s, struct client **client, struct server **server, uint16_t *port) {
109     int cnt, len;
110     unsigned char buf[4], *rad = NULL;
111     struct sockaddr_storage from;
112     struct sockaddr *fromcopy;
113     socklen_t fromlen = sizeof(from);
114     struct clsrvconf *p;
115     struct list_node *node;
116     fd_set readfds;
117     struct client *c = NULL;
118     struct timeval now;
119     
120     for (;;) {
121         if (rad) {
122             free(rad);
123             rad = NULL;
124         }
125         FD_ZERO(&readfds);
126         FD_SET(s, &readfds);
127         if (select(s + 1, &readfds, NULL, NULL, NULL) < 1)
128             continue;
129         cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
130         if (cnt == -1) {
131             debug(DBG_WARN, "radudpget: recv failed");
132             continue;
133         }
134         if (cnt < 20) {
135             debug(DBG_WARN, "radudpget: length too small");
136             recv(s, buf, 4, 0);
137             continue;
138         }
139         
140         p = client
141             ? find_clconf(handle, (struct sockaddr *)&from, NULL)
142             : find_srvconf(handle, (struct sockaddr *)&from, NULL);
143         if (!p) {
144             debug(DBG_WARN, "radudpget: got packet from wrong or unknown UDP peer %s, ignoring", addr2string((struct sockaddr *)&from));
145             recv(s, buf, 4, 0);
146             continue;
147         }
148         
149         len = RADLEN(buf);
150         if (len < 20) {
151             debug(DBG_WARN, "radudpget: length too small");
152             recv(s, buf, 4, 0);
153             continue;
154         }
155             
156         rad = malloc(len);
157         if (!rad) {
158             debug(DBG_ERR, "radudpget: malloc failed");
159             recv(s, buf, 4, 0);
160             continue;
161         }
162         
163         cnt = recv(s, rad, len, MSG_TRUNC);
164         debug(DBG_DBG, "radudpget: got %d bytes from %s", cnt, addr2string((struct sockaddr *)&from));
165
166         if (cnt < len) {
167             debug(DBG_WARN, "radudpget: packet smaller than length field in radius header");
168             continue;
169         }
170         if (cnt > len)
171             debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
172
173         if (client) {
174             *client = NULL;
175             pthread_mutex_lock(p->lock);
176             for (node = list_first(p->clients); node;) {
177                 c = (struct client *)node->data;
178                 node = list_next(node);
179                 if (s != c->sock)
180                     continue;
181                 gettimeofday(&now, NULL);
182                 if (!*client && addr_equal((struct sockaddr *)&from, c->addr)) {
183                     c->expiry = now.tv_sec + 60;
184                     *client = c;
185                 }
186                 if (c->expiry >= now.tv_sec)
187                     continue;
188                 
189                 debug(DBG_DBG, "radudpget: removing expired client (%s)", addr2string(c->addr));
190                 removeudpclientfromreplyq(c);
191                 c->replyq = NULL; /* stop removeclient() from removing common udp replyq */
192                 removelockedclient(c);
193                 break;
194             }
195             if (!*client) {
196                 fromcopy = addr_copy((struct sockaddr *)&from);
197                 if (!fromcopy) {
198                     pthread_mutex_unlock(p->lock);
199                     continue;
200                 }
201                 c = addclient(p, 0);
202                 if (!c) {
203                     free(fromcopy);
204                     pthread_mutex_unlock(p->lock);
205                     continue;
206                 }
207                 c->sock = s;
208                 c->addr = fromcopy;
209                 gettimeofday(&now, NULL);
210                 c->expiry = now.tv_sec + 60;
211                 *client = c;
212             }
213             pthread_mutex_unlock(p->lock);
214         } else if (server)
215             *server = p->servers;
216         break;
217     }
218     if (port)
219         *port = port_get((struct sockaddr *)&from);
220     return rad;
221 }
222
223 int clientradputudp(struct server *server, unsigned char *rad) {
224     size_t len;
225     struct clsrvconf *conf = server->conf;
226     
227     len = RADLEN(rad);
228     if (sendto(server->sock, rad, len, 0, conf->addrinfo->ai_addr, conf->addrinfo->ai_addrlen) >= 0) {
229         debug(DBG_DBG, "clienradputudp: sent UDP of length %d to %s port %d", len, conf->host, port_get(conf->addrinfo->ai_addr));
230         return 1;
231     }
232
233     debug(DBG_WARN, "clientradputudp: send failed");
234     return 0;
235 }
236
237 void *udpclientrd(void *arg) {
238     struct server *server;
239     unsigned char *buf;
240     int *s = (int *)arg;
241     
242     for (;;) {
243         server = NULL;
244         buf = radudpget(*s, NULL, &server, NULL);
245         replyh(server, buf);
246     }
247 }
248
249 void *udpserverrd(void *arg) {
250     struct request *rq;
251     int *sp = (int *)arg;
252     
253     for (;;) {
254         rq = newrequest();
255         if (!rq) {
256             sleep(5); /* malloc failed */
257             continue;
258         }
259         rq->buf = radudpget(*sp, &rq->from, NULL, &rq->udpport);
260         rq->udpsock = *sp;
261         radsrv(rq);
262     }
263     free(sp);
264 }
265
266 void *udpserverwr(void *arg) {
267     struct queue *replyq = (struct queue *)arg;
268     struct request *reply;
269     struct sockaddr_storage to;
270     
271     for (;;) {
272         pthread_mutex_lock(&replyq->mutex);
273         while (!(reply = (struct request *)list_shift(replyq->entries))) {
274             debug(DBG_DBG, "udp server writer, waiting for signal");
275             pthread_cond_wait(&replyq->cond, &replyq->mutex);
276             debug(DBG_DBG, "udp server writer, got signal");
277         }
278         /* do this with lock, udpserverrd may set from = NULL if from expires */
279         if (reply->from)
280             memcpy(&to, reply->from->addr, SOCKADDRP_SIZE(reply->from->addr));
281         pthread_mutex_unlock(&replyq->mutex);
282         if (reply->from) {
283             port_set((struct sockaddr *)&to, reply->udpport);
284             if (sendto(reply->udpsock, reply->replybuf, RADLEN(reply->replybuf), 0, (struct sockaddr *)&to, SOCKADDR_SIZE(to)) < 0)
285                 debug(DBG_WARN, "udpserverwr: send failed");
286         }
287         debug(DBG_DBG, "udpserverwr: refcount %d", reply->refcount);
288         freerq(reply);
289     }
290 }
291
292 void addclientudp(struct client *client) {
293     client->replyq = server_replyq;
294 }
295
296 void addserverextraudp(struct clsrvconf *conf) {
297     switch (conf->addrinfo->ai_family) {
298     case AF_INET:
299         if (client4_sock < 0) {
300             client4_sock = bindtoaddr(srcres, AF_INET, 0, 1);
301             if (client4_sock < 0)
302                 debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->host);
303         }
304         conf->servers->sock = client4_sock;
305         break;
306     case AF_INET6:
307         if (client6_sock < 0) {
308             client6_sock = bindtoaddr(srcres, AF_INET6, 0, 1);
309             if (client6_sock < 0)
310                 debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->host);
311         }
312         conf->servers->sock = client6_sock;
313         break;
314     default:
315         debugx(1, DBG_ERR, "addserver: unsupported address family");
316     }
317 }
318
319 void initextraudp() {
320     pthread_t cl4th, cl6th, srvth;
321
322     if (srcres) {
323         freeaddrinfo(srcres);
324         srcres = NULL;
325     }
326     
327     if (client4_sock >= 0)
328         if (pthread_create(&cl4th, NULL, udpclientrd, (void *)&client4_sock))
329             debugx(1, DBG_ERR, "pthread_create failed");
330     if (client6_sock >= 0)
331         if (pthread_create(&cl6th, NULL, udpclientrd, (void *)&client6_sock))
332             debugx(1, DBG_ERR, "pthread_create failed");
333
334     if (find_clconf_type(handle, NULL)) {
335         server_replyq = newqueue();
336         if (pthread_create(&srvth, NULL, udpserverwr, (void *)server_replyq))
337             debugx(1, DBG_ERR, "pthread_create failed");
338     }
339 }
340 #else
341 const struct protodefs *udpinit(uint8_t h) {
342     return NULL;
343 }
344 #endif