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