cleaning up code
[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 #include <signal.h>
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <netdb.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <limits.h>
16 #ifdef SYS_SOLARIS9
17 #include <fcntl.h>
18 #endif
19 #include <sys/time.h>
20 #include <sys/types.h>
21 #include <sys/select.h>
22 #include <ctype.h>
23 #include <sys/wait.h>
24 #include <arpa/inet.h>
25 #include <regex.h>
26 #include <pthread.h>
27 #include <openssl/ssl.h>
28 #include "debug.h"
29 #include "list.h"
30 #include "util.h"
31 #include "radsecproxy.h"
32 #include "tls.h"
33
34 static int client4_sock = -1;
35 static int client6_sock = -1;
36 static struct queue *server_replyq = NULL;
37
38 static struct addrinfo *srcres = NULL;
39
40 void udpsetsrcres(char *source) {
41     if (!srcres)
42         srcres = resolve_hostport_addrinfo(RAD_UDP, source);
43 }
44
45 void removeudpclientfromreplyq(struct client *c) {
46     struct list_node *n;
47     struct request *r;
48     
49     /* lock the common queue and remove replies for this client */
50     pthread_mutex_lock(&c->replyq->mutex);
51     for (n = list_first(c->replyq->entries); n; n = list_next(n)) {
52         r = (struct request *)n->data;
53         if (r->from == c)
54             r->from = NULL;
55     }
56     pthread_mutex_unlock(&c->replyq->mutex);
57 }       
58
59 /* exactly one of client and server must be non-NULL */
60 /* return who we received from in *client or *server */
61 /* return from in sa if not NULL */
62 unsigned char *radudpget(int s, struct client **client, struct server **server, uint16_t *port) {
63     int cnt, len;
64     unsigned char buf[4], *rad = NULL;
65     struct sockaddr_storage from;
66     struct sockaddr *fromcopy;
67     socklen_t fromlen = sizeof(from);
68     struct clsrvconf *p;
69     struct list_node *node;
70     fd_set readfds;
71     struct client *c = NULL;
72     struct timeval now;
73     
74     for (;;) {
75         if (rad) {
76             free(rad);
77             rad = NULL;
78         }
79         FD_ZERO(&readfds);
80         FD_SET(s, &readfds);
81         if (select(s + 1, &readfds, NULL, NULL, NULL) < 1)
82             continue;
83         cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
84         if (cnt == -1) {
85             debug(DBG_WARN, "radudpget: recv failed");
86             continue;
87         }
88         if (cnt < 20) {
89             debug(DBG_WARN, "radudpget: length too small");
90             recv(s, buf, 4, 0);
91             continue;
92         }
93         
94         p = client
95             ? find_clconf(RAD_UDP, (struct sockaddr *)&from, NULL)
96             : find_srvconf(RAD_UDP, (struct sockaddr *)&from, NULL);
97         if (!p) {
98             debug(DBG_WARN, "radudpget: got packet from wrong or unknown UDP peer %s, ignoring", addr2string((struct sockaddr *)&from));
99             recv(s, buf, 4, 0);
100             continue;
101         }
102         
103         len = RADLEN(buf);
104         if (len < 20) {
105             debug(DBG_WARN, "radudpget: length too small");
106             recv(s, buf, 4, 0);
107             continue;
108         }
109             
110         rad = malloc(len);
111         if (!rad) {
112             debug(DBG_ERR, "radudpget: malloc failed");
113             recv(s, buf, 4, 0);
114             continue;
115         }
116         
117         cnt = recv(s, rad, len, MSG_TRUNC);
118         debug(DBG_DBG, "radudpget: got %d bytes from %s", cnt, addr2string((struct sockaddr *)&from));
119
120         if (cnt < len) {
121             debug(DBG_WARN, "radudpget: packet smaller than length field in radius header");
122             continue;
123         }
124         if (cnt > len)
125             debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
126
127         if (client) {
128             *client = NULL;
129             pthread_mutex_lock(p->lock);
130             for (node = list_first(p->clients); node;) {
131                 c = (struct client *)node->data;
132                 node = list_next(node);
133                 if (s != c->sock)
134                     continue;
135                 gettimeofday(&now, NULL);
136                 if (!*client && addr_equal((struct sockaddr *)&from, c->addr)) {
137                     c->expiry = now.tv_sec + 60;
138                     *client = c;
139                 }
140                 if (c->expiry >= now.tv_sec)
141                     continue;
142                 
143                 debug(DBG_DBG, "radudpget: removing expired client (%s)", addr2string(c->addr));
144                 removeudpclientfromreplyq(c);
145                 c->replyq = NULL; /* stop removeclient() from removing common udp replyq */
146                 removelockedclient(c);
147                 break;
148             }
149             if (!*client) {
150                 fromcopy = addr_copy((struct sockaddr *)&from);
151                 if (!fromcopy) {
152                     pthread_mutex_unlock(p->lock);
153                     continue;
154                 }
155                 c = addclient(p, 0);
156                 if (!c) {
157                     free(fromcopy);
158                     pthread_mutex_unlock(p->lock);
159                     continue;
160                 }
161                 c->sock = s;
162                 c->addr = fromcopy;
163                 gettimeofday(&now, NULL);
164                 c->expiry = now.tv_sec + 60;
165                 *client = c;
166             }
167             pthread_mutex_unlock(p->lock);
168         } else if (server)
169             *server = p->servers;
170         break;
171     }
172     if (port)
173         *port = port_get((struct sockaddr *)&from);
174     return rad;
175 }
176
177 int clientradputudp(struct server *server, unsigned char *rad) {
178     size_t len;
179     struct clsrvconf *conf = server->conf;
180     
181     len = RADLEN(rad);
182     if (sendto(server->sock, rad, len, 0, conf->addrinfo->ai_addr, conf->addrinfo->ai_addrlen) >= 0) {
183         debug(DBG_DBG, "clienradputudp: sent UDP of length %d to %s port %d", len, conf->host, port_get(conf->addrinfo->ai_addr));
184         return 1;
185     }
186
187     debug(DBG_WARN, "clientradputudp: send failed");
188     return 0;
189 }
190
191 void *udpclientrd(void *arg) {
192     struct server *server;
193     unsigned char *buf;
194     int *s = (int *)arg;
195     
196     for (;;) {
197         server = NULL;
198         buf = radudpget(*s, NULL, &server, NULL);
199         replyh(server, buf);
200     }
201 }
202
203 void *udpserverrd(void *arg) {
204     struct request *rq;
205     int *sp = (int *)arg;
206     
207     for (;;) {
208         rq = newrequest();
209         if (!rq) {
210             sleep(5); /* malloc failed */
211             continue;
212         }
213         rq->buf = radudpget(*sp, &rq->from, NULL, &rq->udpport);
214         rq->udpsock = *sp;
215         radsrv(rq);
216     }
217     free(sp);
218 }
219
220 void *udpserverwr(void *arg) {
221     struct queue *replyq = (struct queue *)arg;
222     struct request *reply;
223     struct sockaddr_storage to;
224     
225     for (;;) {
226         pthread_mutex_lock(&replyq->mutex);
227         while (!(reply = (struct request *)list_shift(replyq->entries))) {
228             debug(DBG_DBG, "udp server writer, waiting for signal");
229             pthread_cond_wait(&replyq->cond, &replyq->mutex);
230             debug(DBG_DBG, "udp server writer, got signal");
231         }
232         /* do this with lock, udpserverrd may set from = NULL if from expires */
233         if (reply->from)
234             memcpy(&to, reply->from->addr, SOCKADDRP_SIZE(reply->from->addr));
235         pthread_mutex_unlock(&replyq->mutex);
236         if (reply->from) {
237             port_set((struct sockaddr *)&to, reply->udpport);
238             if (sendto(reply->udpsock, reply->replybuf, RADLEN(reply->replybuf), 0, (struct sockaddr *)&to, SOCKADDR_SIZE(to)) < 0)
239                 debug(DBG_WARN, "udpserverwr: send failed");
240         }
241         debug(DBG_DBG, "udpserverwr: refcount %d", reply->refcount);
242         freerq(reply);
243     }
244 }
245
246 void addclientudp(struct client *client) {
247     client->replyq = server_replyq;
248 }
249
250 void addserverextraudp(struct clsrvconf *conf) {
251     switch (conf->addrinfo->ai_family) {
252     case AF_INET:
253         if (client4_sock < 0) {
254             client4_sock = bindtoaddr(srcres, AF_INET, 0, 1);
255             if (client4_sock < 0)
256                 debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->host);
257         }
258         conf->servers->sock = client4_sock;
259         break;
260     case AF_INET6:
261         if (client6_sock < 0) {
262             client6_sock = bindtoaddr(srcres, AF_INET6, 0, 1);
263             if (client6_sock < 0)
264                 debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->host);
265         }
266         conf->servers->sock = client6_sock;
267         break;
268     default:
269         debugx(1, DBG_ERR, "addserver: unsupported address family");
270     }
271 }
272
273 void initextraudp() {
274     pthread_t cl4th, cl6th, srvth;
275
276     if (srcres) {
277         freeaddrinfo(srcres);
278         srcres = NULL;
279     }
280     
281     if (client4_sock >= 0)
282         if (pthread_create(&cl4th, NULL, udpclientrd, (void *)&client4_sock))
283             debugx(1, DBG_ERR, "pthread_create failed");
284     if (client6_sock >= 0)
285         if (pthread_create(&cl6th, NULL, udpclientrd, (void *)&client6_sock))
286             debugx(1, DBG_ERR, "pthread_create failed");
287
288     if (find_clconf_type(RAD_UDP, NULL)) {
289         server_replyq = newqueue();
290         if (pthread_create(&srvth, NULL, udpserverwr, (void *)server_replyq))
291             debugx(1, DBG_ERR, "pthread_create failed");
292     }
293 }