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