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