2 * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
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.
10 #include <sys/socket.h>
11 #include <netinet/in.h>
20 #include <sys/types.h>
21 #include <sys/select.h>
24 #include <arpa/inet.h>
27 #include <openssl/ssl.h>
31 #include "radsecproxy.h"
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);
40 static const struct protodefs protodefs = {
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 */
52 NULL, /* clientconnreader */
53 clientradputudp, /* clientradput */
54 addclientudp, /* addclient */
55 addserverextraudp, /* addserverextra */
56 udpsetsrcres, /* setsrcres */
57 initextraudp /* initextra */
60 static int client4_sock = -1;
61 static int client6_sock = -1;
62 static struct queue *server_replyq = NULL;
64 static struct addrinfo *srcres = NULL;
65 static uint8_t handle;
67 const struct protodefs *udpinit(uint8_t h) {
72 void udpsetsrcres(char *source) {
74 srcres = resolve_hostport_addrinfo(handle, source);
77 void removeudpclientfromreplyq(struct client *c) {
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;
88 pthread_mutex_unlock(&c->replyq->mutex);
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) {
96 unsigned char buf[4], *rad = NULL;
97 struct sockaddr_storage from;
98 struct sockaddr *fromcopy;
99 socklen_t fromlen = sizeof(from);
101 struct list_node *node;
103 struct client *c = NULL;
113 if (select(s + 1, &readfds, NULL, NULL, NULL) < 1)
115 cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
117 debug(DBG_WARN, "radudpget: recv failed");
121 debug(DBG_WARN, "radudpget: length too small");
127 ? find_clconf(handle, (struct sockaddr *)&from, NULL)
128 : find_srvconf(handle, (struct sockaddr *)&from, NULL);
130 debug(DBG_WARN, "radudpget: got packet from wrong or unknown UDP peer %s, ignoring", addr2string((struct sockaddr *)&from));
137 debug(DBG_WARN, "radudpget: length too small");
144 debug(DBG_ERR, "radudpget: malloc failed");
149 cnt = recv(s, rad, len, MSG_TRUNC);
150 debug(DBG_DBG, "radudpget: got %d bytes from %s", cnt, addr2string((struct sockaddr *)&from));
153 debug(DBG_WARN, "radudpget: packet smaller than length field in radius header");
157 debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
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);
167 gettimeofday(&now, NULL);
168 if (!*client && addr_equal((struct sockaddr *)&from, c->addr)) {
169 c->expiry = now.tv_sec + 60;
172 if (c->expiry >= now.tv_sec)
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);
182 fromcopy = addr_copy((struct sockaddr *)&from);
184 pthread_mutex_unlock(p->lock);
190 pthread_mutex_unlock(p->lock);
195 gettimeofday(&now, NULL);
196 c->expiry = now.tv_sec + 60;
199 pthread_mutex_unlock(p->lock);
201 *server = p->servers;
205 *port = port_get((struct sockaddr *)&from);
209 int clientradputudp(struct server *server, unsigned char *rad) {
211 struct clsrvconf *conf = server->conf;
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));
219 debug(DBG_WARN, "clientradputudp: send failed");
223 void *udpclientrd(void *arg) {
224 struct server *server;
230 buf = radudpget(*s, NULL, &server, NULL);
235 void *udpserverrd(void *arg) {
237 int *sp = (int *)arg;
242 sleep(5); /* malloc failed */
245 rq->buf = radudpget(*sp, &rq->from, NULL, &rq->udpport);
252 void *udpserverwr(void *arg) {
253 struct queue *replyq = (struct queue *)arg;
254 struct request *reply;
255 struct sockaddr_storage to;
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");
264 /* do this with lock, udpserverrd may set from = NULL if from expires */
266 memcpy(&to, reply->from->addr, SOCKADDRP_SIZE(reply->from->addr));
267 pthread_mutex_unlock(&replyq->mutex);
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");
273 debug(DBG_DBG, "udpserverwr: refcount %d", reply->refcount);
278 void addclientudp(struct client *client) {
279 client->replyq = server_replyq;
282 void addserverextraudp(struct clsrvconf *conf) {
283 switch (conf->addrinfo->ai_family) {
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);
290 conf->servers->sock = client4_sock;
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);
298 conf->servers->sock = client6_sock;
301 debugx(1, DBG_ERR, "addserver: unsupported address family");
305 void initextraudp() {
306 pthread_t cl4th, cl6th, srvth;
309 freeaddrinfo(srcres);
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");
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");