1 /* Copyright (c) 2006-2010, UNINETT AS
2 * Copyright (c) 2010-2012, NORDUnet A/S */
3 /* See LICENSE for licensing information. */
6 #include <sys/socket.h>
7 #include <netinet/in.h>
16 #include <sys/types.h>
17 #include <sys/select.h>
20 #include <arpa/inet.h>
24 #include "radsecproxy.h"
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);
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 setprotoopts, /* setprotoopts */
51 getlistenerargs, /* getlistenerargs */
52 udpserverrd, /* listener */
54 NULL, /* clientconnreader */
55 clientradputudp, /* clientradput */
56 addclientudp, /* addclient */
57 addserverextraudp, /* addserverextra */
58 udpsetsrcres, /* setsrcres */
59 initextraudp /* initextra */
62 static int client4_sock = -1;
63 static int client6_sock = -1;
64 static struct gqueue *server_replyq = NULL;
66 static struct addrinfo *srcres = NULL;
67 static uint8_t handle;
68 static struct commonprotoopts *protoopts = NULL;
70 const struct protodefs *udpinit(uint8_t h) {
75 static void setprotoopts(struct commonprotoopts *opts) {
79 static char **getlistenerargs() {
80 return protoopts ? protoopts->listenargs : NULL;
86 resolvepassiveaddrinfo(protoopts ? protoopts->sourcearg : NULL,
87 AF_UNSPEC, NULL, protodefs.socktype);
90 void removeudpclientfromreplyq(struct client *c) {
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;
101 pthread_mutex_unlock(&c->replyq->mutex);
104 static int addr_equal(struct sockaddr *a, struct sockaddr *b) {
105 switch (a->sa_family) {
107 return !memcmp(&((struct sockaddr_in*)a)->sin_addr,
108 &((struct sockaddr_in*)b)->sin_addr,
109 sizeof(struct in_addr));
111 return IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6*)a)->sin6_addr,
112 &((struct sockaddr_in6*)b)->sin6_addr);
119 uint16_t port_get(struct sockaddr *sa) {
120 switch (sa->sa_family) {
122 return ntohs(((struct sockaddr_in *)sa)->sin_port);
124 return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
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) {
134 unsigned char buf[4], *rad = NULL;
135 struct sockaddr_storage from;
136 struct sockaddr *fromcopy;
137 socklen_t fromlen = sizeof(from);
139 struct list_node *node;
141 struct client *c = NULL;
151 if (select(s + 1, &readfds, NULL, NULL, NULL) < 1)
153 cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
155 debug(DBG_WARN, "radudpget: recv failed");
160 ? find_clconf(handle, (struct sockaddr *)&from, NULL)
161 : find_srvconf(handle, (struct sockaddr *)&from, NULL);
163 debug(DBG_WARN, "radudpget: got packet from wrong or unknown UDP peer %s, ignoring", addr2string((struct sockaddr *)&from));
170 debug(DBG_WARN, "radudpget: length too small");
177 debug(DBG_ERR, "radudpget: malloc failed");
182 cnt = recv(s, rad, len, MSG_TRUNC);
183 debug(DBG_DBG, "radudpget: got %d bytes from %s", cnt, addr2string((struct sockaddr *)&from));
186 debug(DBG_WARN, "radudpget: packet smaller than length field in radius header");
190 debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
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);
200 gettimeofday(&now, NULL);
201 if (!*client && addr_equal((struct sockaddr *)&from, c->addr)) {
202 c->expiry = now.tv_sec + 60;
205 if (c->expiry >= now.tv_sec)
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);
215 fromcopy = addr_copy((struct sockaddr *)&from);
217 pthread_mutex_unlock(p->lock);
223 pthread_mutex_unlock(p->lock);
228 gettimeofday(&now, NULL);
229 c->expiry = now.tv_sec + 60;
232 pthread_mutex_unlock(p->lock);
234 *server = p->servers;
238 *port = port_get((struct sockaddr *)&from);
242 int clientradputudp(struct server *server, unsigned char *rad) {
244 struct clsrvconf *conf = server->conf;
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));
254 debug(DBG_WARN, "clientradputudp: send failed");
258 void *udpclientrd(void *arg) {
259 struct server *server;
265 buf = radudpget(*s, NULL, &server, NULL);
270 void *udpserverrd(void *arg) {
272 int *sp = (int *)arg;
277 sleep(5); /* malloc failed */
280 rq->buf = radudpget(*sp, &rq->from, NULL, &rq->udpport);
288 void *udpserverwr(void *arg) {
289 struct gqueue *replyq = (struct gqueue *)arg;
290 struct request *reply;
291 struct sockaddr_storage to;
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");
300 /* do this with lock, udpserverrd may set from = NULL if from expires */
302 memcpy(&to, reply->from->addr, SOCKADDRP_SIZE(reply->from->addr));
303 pthread_mutex_unlock(&replyq->mutex);
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");
309 debug(DBG_DBG, "udpserverwr: refcount %d", reply->refcount);
314 void addclientudp(struct client *client) {
315 client->replyq = server_replyq;
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) {
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);
327 conf->servers->sock = client4_sock;
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);
335 conf->servers->sock = client6_sock;
338 debugx(1, DBG_ERR, "addserver: unsupported address family");
342 void initextraudp() {
343 pthread_t cl4th, cl6th, srvth;
346 freeaddrinfo(srcres);
350 if (client4_sock >= 0)
351 if (pthread_create(&cl4th, NULL, udpclientrd, (void *)&client4_sock))
352 debugx(1, DBG_ERR, "pthread_create failed");
353 if (client6_sock >= 0)
354 if (pthread_create(&cl6th, NULL, udpclientrd, (void *)&client6_sock))
355 debugx(1, DBG_ERR, "pthread_create failed");
357 if (find_clconf_type(handle, NULL)) {
358 server_replyq = newqueue();
359 if (pthread_create(&srvth, NULL, udpserverwr, (void *)server_replyq))
360 debugx(1, DBG_ERR, "pthread_create failed");
364 const struct protodefs *udpinit(uint8_t h) {
369 /* Local Variables: */
370 /* c-file-style: "stroustrup" */