separated udp
[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 /* exactly one of client and server must be non-NULL */
35 /* return who we received from in *client or *server */
36 /* return from in sa if not NULL */
37 unsigned char *radudpget(int s, struct client **client, struct server **server, struct sockaddr_storage *sa) {
38     int cnt, len;
39     unsigned char buf[4], *rad = NULL;
40     struct sockaddr_storage from;
41     socklen_t fromlen = sizeof(from);
42     struct clsrvconf *p;
43     struct list_node *node;
44     fd_set readfds;
45     
46     for (;;) {
47         if (rad) {
48             free(rad);
49             rad = NULL;
50         }
51         FD_ZERO(&readfds);
52         FD_SET(s, &readfds);
53         if (select(s + 1, &readfds, NULL, NULL, NULL) < 1)
54             continue;
55         cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
56         if (cnt == -1) {
57             debug(DBG_WARN, "radudpget: recv failed");
58             continue;
59         }
60         if (cnt < 20) {
61             debug(DBG_WARN, "radudpget: length too small");
62             recv(s, buf, 4, 0);
63             continue;
64         }
65         
66         p = client
67             ? find_clconf(RAD_UDP, (struct sockaddr *)&from, NULL)
68             : find_srvconf(RAD_UDP, (struct sockaddr *)&from, NULL);
69         if (!p) {
70             debug(DBG_WARN, "radudpget: got packet from wrong or unknown UDP peer %s, ignoring", addr2string((struct sockaddr *)&from, fromlen));
71             recv(s, buf, 4, 0);
72             continue;
73         }
74         
75         len = RADLEN(buf);
76         if (len < 20) {
77             debug(DBG_WARN, "radudpget: length too small");
78             recv(s, buf, 4, 0);
79             continue;
80         }
81             
82         rad = malloc(len);
83         if (!rad) {
84             debug(DBG_ERR, "radudpget: malloc failed");
85             recv(s, buf, 4, 0);
86             continue;
87         }
88         
89         cnt = recv(s, rad, len, MSG_TRUNC);
90         debug(DBG_DBG, "radudpget: got %d bytes from %s", cnt, addr2string((struct sockaddr *)&from, fromlen));
91
92         if (cnt < len) {
93             debug(DBG_WARN, "radudpget: packet smaller than length field in radius header");
94             continue;
95         }
96         if (cnt > len)
97             debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
98
99         if (client) {
100             node = list_first(p->clients);
101             *client = node ? (struct client *)node->data : addclient(p);
102             if (!*client)
103                 continue;
104         } else if (server)
105             *server = p->servers;
106         break;
107     }
108     if (sa)
109         *sa = from;
110     return rad;
111 }
112
113 int clientradputudp(struct server *server, unsigned char *rad) {
114     size_t len;
115     struct sockaddr_storage sa;
116     struct sockaddr *sap;
117     struct clsrvconf *conf = server->conf;
118     in_port_t *port = NULL;
119     
120     len = RADLEN(rad);
121     
122     if (*rad == RAD_Accounting_Request) {
123         sap = (struct sockaddr *)&sa;
124         memcpy(sap, conf->addrinfo->ai_addr, conf->addrinfo->ai_addrlen);
125     } else
126         sap = conf->addrinfo->ai_addr;
127     
128     switch (sap->sa_family) {
129     case AF_INET:
130         port = &((struct sockaddr_in *)sap)->sin_port;
131         break;
132     case AF_INET6:
133         port = &((struct sockaddr_in6 *)sap)->sin6_port;
134         break;
135     default:
136         return 0;
137     }
138
139     if (*rad == RAD_Accounting_Request)
140         *port = htons(ntohs(*port) + 1);
141     
142     if (sendto(server->sock, rad, len, 0, sap, conf->addrinfo->ai_addrlen) >= 0) {
143         debug(DBG_DBG, "clienradputudp: sent UDP of length %d to %s port %d", len, conf->host, ntohs(*port));
144         return 1;
145     }
146
147     debug(DBG_WARN, "clientradputudp: send failed");
148     return 0;
149 }
150
151 void *udpclientrd(void *arg) {
152     struct server *server;
153     unsigned char *buf;
154     int *s = (int *)arg;
155     
156     for (;;) {
157         server = NULL;
158         buf = radudpget(*s, NULL, &server, NULL);
159         if (!replyh(server, buf))
160             free(buf);
161     }
162 }
163
164 void *udpserverrd(void *arg) {
165     struct request rq;
166     int *sp = (int *)arg;
167     
168     for (;;) {
169         memset(&rq, 0, sizeof(struct request));
170         rq.buf = radudpget(*sp, &rq.from, NULL, &rq.fromsa);
171         rq.fromudpsock = *sp;
172         radsrv(&rq);
173     }
174     free(sp);
175 }
176
177 void *udpserverwr(void *arg) {
178     struct queue *replyq = (struct queue *)arg;
179     struct reply *reply;
180     
181     for (;;) {
182         pthread_mutex_lock(&replyq->mutex);
183         while (!(reply = (struct reply *)list_shift(replyq->entries))) {
184             debug(DBG_DBG, "udp server writer, waiting for signal");
185             pthread_cond_wait(&replyq->cond, &replyq->mutex);
186             debug(DBG_DBG, "udp server writer, got signal");
187         }
188         pthread_mutex_unlock(&replyq->mutex);
189
190         if (sendto(reply->toudpsock, reply->buf, RADLEN(reply->buf), 0,
191                    (struct sockaddr *)&reply->tosa, SOCKADDR_SIZE(reply->tosa)) < 0)
192             debug(DBG_WARN, "sendudp: send failed");
193         free(reply->buf);
194         free(reply);
195     }
196 }