WIP: Fix the Proxy-State issue.
[libradsec.git] / udp.c
1 /* Copyright (c) 2007-2009, UNINETT AS
2  * Copyright (c) 2012, NORDUnet A/S */
3 /* See LICENSE for licensing information. */
4
5 #include <signal.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <netdb.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <limits.h>
12 #ifdef SYS_SOLARIS9
13 #include <fcntl.h>
14 #endif
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <sys/select.h>
18 #include <ctype.h>
19 #include <sys/wait.h>
20 #include <arpa/inet.h>
21 #include <regex.h>
22 #include <pthread.h>
23 #include <assert.h>
24 #include "radsecproxy.h"
25 #include "hostport.h"
26
27 #ifdef RADPROT_UDP
28 #include "debug.h"
29 #include "util.h"
30
31 static void setprotoopts(struct commonprotoopts *opts);
32 static char **getlistenerargs();
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();
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     setprotoopts, /* setprotoopts */
51     getlistenerargs, /* getlistenerargs */
52     udpserverrd, /* listener */
53     NULL, /* connecter */
54     NULL, /* clientconnreader */
55     clientradputudp, /* clientradput */
56     addclientudp, /* addclient */
57     addserverextraudp, /* addserverextra */
58     udpsetsrcres, /* setsrcres */
59     initextraudp /* initextra */
60 };
61
62 static int client4_sock = -1;
63 static int client6_sock = -1;
64 static struct gqueue *server_replyq = NULL;
65
66 static struct addrinfo *srcres = NULL;
67 static uint8_t handle;
68 static struct commonprotoopts *protoopts = NULL;
69
70 const struct protodefs *udpinit(uint8_t h) {
71     handle = h;
72     return &protodefs;
73 }
74
75 static void setprotoopts(struct commonprotoopts *opts) {
76     protoopts = opts;
77 }
78
79 static char **getlistenerargs() {
80     return protoopts ? protoopts->listenargs : NULL;
81 }
82
83 void udpsetsrcres() {
84     if (!srcres)
85         srcres =
86             resolvepassiveaddrinfo(protoopts ? protoopts->sourcearg : NULL,
87                                    AF_UNSPEC, NULL, protodefs.socktype);
88 }
89
90 void removeudpclientfromreplyq(struct client *c) {
91     struct list_node *n;
92     struct request *r;
93
94     /* lock the common queue and remove replies for this client */
95     pthread_mutex_lock(&c->replyq->mutex);
96     for (n = list_first(c->replyq->entries); n; n = list_next(n)) {
97         r = (struct request *)n->data;
98         if (r->from == c)
99             r->from = NULL;
100     }
101     pthread_mutex_unlock(&c->replyq->mutex);
102 }
103
104 static int addr_equal(struct sockaddr *a, struct sockaddr *b) {
105     switch (a->sa_family) {
106     case AF_INET:
107         return !memcmp(&((struct sockaddr_in*)a)->sin_addr,
108                        &((struct sockaddr_in*)b)->sin_addr,
109                        sizeof(struct in_addr));
110     case AF_INET6:
111         return IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6*)a)->sin6_addr,
112                                   &((struct sockaddr_in6*)b)->sin6_addr);
113     default:
114         /* Must not reach */
115         return 0;
116     }
117 }
118
119 uint16_t port_get(struct sockaddr *sa) {
120     switch (sa->sa_family) {
121     case AF_INET:
122         return ntohs(((struct sockaddr_in *)sa)->sin_port);
123     case AF_INET6:
124         return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
125     }
126     return 0;
127 }
128
129 /* exactly one of client and server must be non-NULL */
130 /* return who we received from in *client or *server */
131 /* return from in sa if not NULL */
132 unsigned char *radudpget(int s, struct client **client, struct server **server, uint16_t *port) {
133     int cnt, len;
134     unsigned char buf[4], *rad = NULL;
135     struct sockaddr_storage from;
136     struct sockaddr *fromcopy;
137     socklen_t fromlen = sizeof(from);
138     struct clsrvconf *p;
139     struct list_node *node;
140     fd_set readfds;
141     struct client *c = NULL;
142     struct timeval now;
143
144     for (;;) {
145         if (rad) {
146             free(rad);
147             rad = NULL;
148         }
149         FD_ZERO(&readfds);
150         FD_SET(s, &readfds);
151         if (select(s + 1, &readfds, NULL, NULL, NULL) < 1)
152             continue;
153         cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
154         if (cnt == -1) {
155             debug(DBG_WARN, "radudpget: recv failed");
156             continue;
157         }
158
159         p = client
160             ? find_clconf(handle, (struct sockaddr *)&from, NULL)
161             : find_srvconf(handle, (struct sockaddr *)&from, NULL);
162         if (!p) {
163             debug(DBG_WARN, "radudpget: got packet from wrong or unknown UDP peer %s, ignoring", addr2string((struct sockaddr *)&from));
164             recv(s, buf, 4, 0);
165             continue;
166         }
167
168         len = RADLEN(buf);
169         if (len < 20) {
170             debug(DBG_WARN, "radudpget: length too small");
171             recv(s, buf, 4, 0);
172             continue;
173         }
174
175         rad = malloc(len);
176         if (!rad) {
177             debug(DBG_ERR, "radudpget: malloc failed");
178             recv(s, buf, 4, 0);
179             continue;
180         }
181
182         cnt = recv(s, rad, len, MSG_TRUNC);
183         debug(DBG_DBG, "radudpget: got %d bytes from %s", cnt, addr2string((struct sockaddr *)&from));
184
185         if (cnt < len) {
186             debug(DBG_WARN, "radudpget: packet smaller than length field in radius header");
187             continue;
188         }
189         if (cnt > len)
190             debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
191
192         if (client) {
193             *client = NULL;
194             pthread_mutex_lock(p->lock);
195             for (node = list_first(p->clients); node;) {
196                 c = (struct client *)node->data;
197                 node = list_next(node);
198                 if (s != c->sock)
199                     continue;
200                 gettimeofday(&now, NULL);
201                 if (!*client && addr_equal((struct sockaddr *)&from, c->addr)) {
202                     c->expiry = now.tv_sec + 60;
203                     *client = c;
204                 }
205                 if (c->expiry >= now.tv_sec)
206                     continue;
207
208                 debug(DBG_DBG, "radudpget: removing expired client (%s)", addr2string(c->addr));
209                 removeudpclientfromreplyq(c);
210                 c->replyq = NULL; /* stop removeclient() from removing common udp replyq */
211                 removelockedclient(c);
212                 break;
213             }
214             if (!*client) {
215                 fromcopy = addr_copy((struct sockaddr *)&from);
216                 if (!fromcopy) {
217                     pthread_mutex_unlock(p->lock);
218                     continue;
219                 }
220                 c = addclient(p, 0);
221                 if (!c) {
222                     free(fromcopy);
223                     pthread_mutex_unlock(p->lock);
224                     continue;
225                 }
226                 c->sock = s;
227                 c->addr = fromcopy;
228                 gettimeofday(&now, NULL);
229                 c->expiry = now.tv_sec + 60;
230                 *client = c;
231             }
232             pthread_mutex_unlock(p->lock);
233         } else if (server)
234             *server = p->servers;
235         break;
236     }
237     if (port)
238         *port = port_get((struct sockaddr *)&from);
239     return rad;
240 }
241
242 int clientradputudp(struct server *server, unsigned char *rad) {
243     size_t len;
244     struct clsrvconf *conf = server->conf;
245     struct addrinfo *ai;
246
247     len = RADLEN(rad);
248     ai = ((struct hostportres *)list_first(conf->hostports)->data)->addrinfo;
249     if (sendto(server->sock, rad, len, 0, ai->ai_addr, ai->ai_addrlen) >= 0) {
250         debug(DBG_DBG, "clienradputudp: sent UDP of length %d to %s port %d", len, addr2string(ai->ai_addr), port_get(ai->ai_addr));
251         return 1;
252     }
253
254     debug(DBG_WARN, "clientradputudp: send failed");
255     return 0;
256 }
257
258 void *udpclientrd(void *arg) {
259     struct server *server;
260     unsigned char *buf;
261     int *s = (int *)arg;
262
263     for (;;) {
264         server = NULL;
265         buf = radudpget(*s, NULL, &server, NULL);
266         replyh(server, buf);
267     }
268 }
269
270 void *udpserverrd(void *arg) {
271     struct request *rq;
272     int *sp = (int *)arg;
273
274     for (;;) {
275         rq = newrequest();
276         if (!rq) {
277             sleep(5); /* malloc failed */
278             continue;
279         }
280         rq->buf = radudpget(*sp, &rq->from, NULL, &rq->udpport);
281         rq->udpsock = *sp;
282         radsrv(rq);
283     }
284     free(sp);
285     return NULL;
286 }
287
288 void *udpserverwr(void *arg) {
289     struct gqueue *replyq = (struct gqueue *)arg;
290     struct request *reply;
291     struct sockaddr_storage to;
292
293     for (;;) {
294         pthread_mutex_lock(&replyq->mutex);
295         while (!(reply = (struct request *)list_shift(replyq->entries))) {
296             debug(DBG_DBG, "udp server writer, waiting for signal");
297             pthread_cond_wait(&replyq->cond, &replyq->mutex);
298             debug(DBG_DBG, "udp server writer, got signal");
299         }
300         /* do this with lock, udpserverrd may set from = NULL if from expires */
301         if (reply->from)
302             memcpy(&to, reply->from->addr, SOCKADDRP_SIZE(reply->from->addr));
303         pthread_mutex_unlock(&replyq->mutex);
304         if (reply->from) {
305             port_set((struct sockaddr *)&to, reply->udpport);
306             if (sendto(reply->udpsock, reply->replybuf, RADLEN(reply->replybuf), 0, (struct sockaddr *)&to, SOCKADDR_SIZE(to)) < 0)
307                 debug(DBG_WARN, "udpserverwr: send failed");
308         }
309         debug(DBG_DBG, "udpserverwr: refcount %d", reply->refcount);
310         freerq(reply);
311     }
312 }
313
314 void addclientudp(struct client *client) {
315     client->replyq = server_replyq;
316 }
317
318 void addserverextraudp(struct clsrvconf *conf) {
319     assert(list_first(conf->hostports) != NULL);
320     switch (((struct hostportres *)list_first(conf->hostports)->data)->addrinfo->ai_family) {
321     case AF_INET:
322         if (client4_sock < 0) {
323             client4_sock = bindtoaddr(srcres, AF_INET, 0, 1);
324             if (client4_sock < 0)
325                 debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->name);
326         }
327         conf->servers->sock = client4_sock;
328         break;
329     case AF_INET6:
330         if (client6_sock < 0) {
331             client6_sock = bindtoaddr(srcres, AF_INET6, 0, 1);
332             if (client6_sock < 0)
333                 debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->name);
334         }
335         conf->servers->sock = client6_sock;
336         break;
337     default:
338         debugx(1, DBG_ERR, "addserver: unsupported address family");
339     }
340 }
341
342 void initextraudp() {
343     pthread_t cl4th, cl6th, srvth;
344
345     if (srcres) {
346         freeaddrinfo(srcres);
347         srcres = NULL;
348     }
349
350     if (client4_sock >= 0)
351         if (pthread_create(&cl4th, &pthread_attr, udpclientrd, (void *)&client4_sock))
352             debugx(1, DBG_ERR, "pthread_create failed");
353     if (client6_sock >= 0)
354         if (pthread_create(&cl6th, &pthread_attr, udpclientrd, (void *)&client6_sock))
355             debugx(1, DBG_ERR, "pthread_create failed");
356
357     if (find_clconf_type(handle, NULL)) {
358         server_replyq = newqueue();
359         if (pthread_create(&srvth, &pthread_attr, udpserverwr, (void *)server_replyq))
360             debugx(1, DBG_ERR, "pthread_create failed");
361     }
362 }
363 #else
364 const struct protodefs *udpinit(uint8_t h) {
365     return NULL;
366 }
367 #endif
368
369 /* Local Variables: */
370 /* c-file-style: "stroustrup" */
371 /* End: */