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