1 /* Copyright (c) 2007-2009, UNINETT AS
2 * Copyright (c) 2012, NORDUnet A/S */
3 /* See LICENSE for licensing information. */
4
5 #include <signal.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <netdb.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <limits.h>
12 #ifdef SYS_SOLARIS9
13 #include <fcntl.h>
14 #endif
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <poll.h>
18 #include <ctype.h>
19 #include <sys/wait.h>
20 #include <arpa/inet.h>
21 #include <regex.h>
22 #include <pthread.h>
23 #include "radsecproxy.h"
24 #include "hostport.h"
25
26 #ifdef RADPROT_TCP
27 #include "debug.h"
28 #include "util.h"
29 static void setprotoopts(struct commonprotoopts *opts);
30 static char **getlistenerargs();
31 void *tcplistener(void *arg);
32 int tcpconnect(struct server *server, int timeout, char * text);
33 void *tcpclientrd(void *arg);
34 int clientradputtcp(struct server *server, unsigned char *rad);
35 void tcpsetsrcres();
36
37 static const struct protodefs protodefs = {
38 "tcp",
39 NULL, /* secretdefault */
40 SOCK_STREAM, /* socktype */
41 "1812", /* portdefault */
42 0, /* retrycountdefault */
43 0, /* retrycountmax */
44 REQUEST_RETRY_INTERVAL * REQUEST_RETRY_COUNT, /* retryintervaldefault */
45 60, /* retryintervalmax */
46 DUPLICATE_INTERVAL, /* duplicateintervaldefault */
47 setprotoopts, /* setprotoopts */
48 getlistenerargs, /* getlistenerargs */
49 tcplistener, /* listener */
50 tcpconnect, /* connecter */
51 tcpclientrd, /* clientconnreader */
52 clientradputtcp, /* clientradput */
53 NULL, /* addclient */
54 NULL, /* addserverextra */
55 tcpsetsrcres, /* setsrcres */
56 NULL /* initextra */
57 };
58
59 static struct addrinfo *srcres = NULL;
60 static uint8_t handle;
61 static struct commonprotoopts *protoopts = NULL;
tcpinit(uint8_t h)62 const struct protodefs *tcpinit(uint8_t h) {
63 handle = h;
64 return &protodefs;
65 }
66
setprotoopts(struct commonprotoopts * opts)67 static void setprotoopts(struct commonprotoopts *opts) {
68 protoopts = opts;
69 }
70
getlistenerargs()71 static char **getlistenerargs() {
72 return protoopts ? protoopts->listenargs : NULL;
73 }
74
tcpsetsrcres()75 void tcpsetsrcres() {
76 if (!srcres)
77 srcres =
78 resolvepassiveaddrinfo(protoopts ? protoopts->sourcearg : NULL,
79 AF_UNSPEC, NULL, protodefs.socktype);
80 }
81
tcpconnect(struct server * server,int timeout,char * text)82 int tcpconnect(struct server *server, int timeout, char *text) {
83 struct timeval now, start;
84 int firsttry = 1;
85 time_t wait;
86
87 debug(DBG_DBG, "tcpconnect: called from %s", text);
88 pthread_mutex_lock(&server->lock);
89
90 if (server->state == RSP_SERVER_STATE_CONNECTED)
91 server->state = RSP_SERVER_STATE_RECONNECTING;
92
93 gettimeofday(&start, NULL);
94
95 for (;;) {
96 if (server->sock >= 0)
97 close(server->sock);
98 server->sock = -1;
99
100 pthread_mutex_unlock(&server->lock);
101 wait = connect_wait(start, server->connecttime, firsttry);
102 debug(DBG_INFO, "Next connection attempt to %s in %lds", server->conf->name, wait);
103 sleep(wait);
104 firsttry = 0;
105
106 gettimeofday(&now, NULL);
107 if (timeout && (now.tv_sec - start.tv_sec) > timeout) {
108 debug(DBG_DBG, "tcpconnect: timeout");
109 return 0;
110 }
111 pthread_mutex_lock(&server->lock);
112
113 debug(DBG_INFO, "tcpconnect: connecting to %s", server->conf->name);
114 if ((server->sock = connecttcphostlist(server->conf->hostports, srcres)) < 0)
115 continue;
116 if (server->conf->keepalive)
117 enable_keepalive(server->sock);
118 break;
119 }
120 server->state = RSP_SERVER_STATE_CONNECTED;
121 gettimeofday(&server->connecttime, NULL);
122 server->lostrqs = 0;
123 pthread_mutex_unlock(&server->lock);
124 pthread_mutex_lock(&server->newrq_mutex);
125 server->conreset = 1;
126 pthread_cond_signal(&server->newrq_cond);
127 pthread_mutex_unlock(&server->newrq_mutex);
128 return 1;
129 }
130
131 /* timeout in seconds, 0 means no timeout (blocking), returns when num bytes have been read, or timeout */
132 /* returns 0 on timeout, -1 on error and num if ok */
tcpreadtimeout(int s,unsigned char * buf,int num,int timeout)133 int tcpreadtimeout(int s, unsigned char *buf, int num, int timeout) {
134 int ndesc, cnt, len;
135 struct pollfd fds[1];
136
137 if (s < 0)
138 return -1;
139 /* make socket non-blocking? */
140 for (len = 0; len < num; len += cnt) {
141 fds[0].fd = s;
142 fds[0].events = POLLIN;
143 ndesc = poll(fds, 1, timeout? timeout * 1000 : -1);
144 if (ndesc < 1)
145 return ndesc;
146
147 if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL) ) {
148 return -1;
149 }
150 cnt = read(s, buf + len, num - len);
151 if (cnt <= 0)
152 return -1;
153 }
154 return num;
155 }
156
157 /* timeout in seconds, 0 means no timeout (blocking) */
radtcpget(int s,int timeout)158 unsigned char *radtcpget(int s, int timeout) {
159 int cnt, len;
160 unsigned char buf[4], *rad;
161
162 for (;;) {
163 cnt = tcpreadtimeout(s, buf, 4, timeout);
164 if (cnt < 1) {
165 debug(DBG_DBG, cnt ? "radtcpget: connection lost" : "radtcpget: timeout");
166 return NULL;
167 }
168
169 len = RADLEN(buf);
170 if (len < 4) {
171 debug(DBG_ERR, "radtcpget: length too small");
172 continue;
173 }
174 rad = malloc(len);
175 if (!rad) {
176 debug(DBG_ERR, "radtcpget: malloc failed");
177 continue;
178 }
179 memcpy(rad, buf, 4);
180
181 cnt = tcpreadtimeout(s, rad + 4, len - 4, timeout);
182 if (cnt < 1) {
183 debug(DBG_DBG, cnt ? "radtcpget: connection lost" : "radtcpget: timeout");
184 free(rad);
185 return NULL;
186 }
187
188 if (len >= 20)
189 break;
190
191 free(rad);
192 debug(DBG_WARN, "radtcpget: packet smaller than minimum radius size");
193 }
194
195 debug(DBG_DBG, "radtcpget: got %d bytes", len);
196 return rad;
197 }
198
clientradputtcp(struct server * server,unsigned char * rad)199 int clientradputtcp(struct server *server, unsigned char *rad) {
200 int cnt;
201 size_t len;
202 struct clsrvconf *conf = server->conf;
203
204 if (server->state != RSP_SERVER_STATE_CONNECTED)
205 return 0;
206 len = RADLEN(rad);
207 if ((cnt = write(server->sock, rad, len)) <= 0) {
208 debug(DBG_ERR, "clientradputtcp: write error");
209 return 0;
210 }
211 debug(DBG_DBG, "clientradputtcp: Sent %d bytes, Radius packet of length %d to TCP peer %s", cnt, len, conf->name);
212 return 1;
213 }
214
tcpclientrd(void * arg)215 void *tcpclientrd(void *arg) {
216 struct server *server = (struct server *)arg;
217 unsigned char *buf;
218
219 for (;;) {
220 buf = radtcpget(server->sock, server->dynamiclookuparg ? IDLE_TIMEOUT : 0);
221 if (!buf) {
222 if (server->dynamiclookuparg)
223 break;
224 tcpconnect(server, 0, "tcpclientrd");
225 continue;
226 }
227
228 replyh(server, buf);
229 }
230 server->clientrdgone = 1;
231 pthread_mutex_lock(&server->newrq_mutex);
232 pthread_cond_signal(&server->newrq_cond);
233 pthread_mutex_unlock(&server->newrq_mutex);
234 return NULL;
235 }
236
tcpserverwr(void * arg)237 void *tcpserverwr(void *arg) {
238 int cnt;
239 struct client *client = (struct client *)arg;
240 struct gqueue *replyq;
241 struct request *reply;
242 char tmp[INET6_ADDRSTRLEN];
243
244 debug(DBG_DBG, "tcpserverwr: starting for %s", addr2string(client->addr, tmp, sizeof(tmp)));
245 replyq = client->replyq;
246 for (;;) {
247 pthread_mutex_lock(&replyq->mutex);
248 while (!list_first(replyq->entries)) {
249 if (client->sock >= 0) {
250 debug(DBG_DBG, "tcpserverwr: waiting for signal");
251 pthread_cond_wait(&replyq->cond, &replyq->mutex);
252 debug(DBG_DBG, "tcpserverwr: got signal");
253 }
254 if (client->sock < 0) {
255 /* s might have changed while waiting */
256 pthread_mutex_unlock(&replyq->mutex);
257 debug(DBG_DBG, "tcpserverwr: exiting as requested");
258 pthread_exit(NULL);
259 }
260 }
261 reply = (struct request *)list_shift(replyq->entries);
262 pthread_mutex_unlock(&replyq->mutex);
263 cnt = write(client->sock, reply->replybuf, RADLEN(reply->replybuf));
264 if (cnt > 0)
265 debug(DBG_DBG, "tcpserverwr: sent %d bytes, Radius packet of length %d to %s",
266 cnt, RADLEN(reply->replybuf), addr2string(client->addr, tmp, sizeof(tmp)));
267 else
268 debug(DBG_ERR, "tcpserverwr: write error for %s", addr2string(client->addr, tmp, sizeof(tmp)));
269 freerq(reply);
270 }
271 }
272
tcpserverrd(struct client * client)273 void tcpserverrd(struct client *client) {
274 struct request *rq;
275 uint8_t *buf;
276 pthread_t tcpserverwrth;
277 char tmp[INET6_ADDRSTRLEN];
278
279 debug(DBG_DBG, "tcpserverrd: starting for %s", addr2string(client->addr, tmp, sizeof(tmp)));
280
281 if (pthread_create(&tcpserverwrth, &pthread_attr, tcpserverwr, (void *)client)) {
282 debug(DBG_ERR, "tcpserverrd: pthread_create failed");
283 return;
284 }
285
286 for (;;) {
287 buf = radtcpget(client->sock, 0);
288 if (!buf) {
289 debug(DBG_ERR, "tcpserverrd: connection from %s lost", addr2string(client->addr, tmp, sizeof(tmp)));
290 break;
291 }
292 debug(DBG_DBG, "tcpserverrd: got Radius message from %s", addr2string(client->addr, tmp, sizeof(tmp)));
293 rq = newrequest();
294 if (!rq) {
295 free(buf);
296 continue;
297 }
298 rq->buf = buf;
299 rq->from = client;
300 if (!radsrv(rq)) {
301 debug(DBG_ERR, "tcpserverrd: message authentication/validation failed, closing connection from %s", addr2string(client->addr, tmp, sizeof(tmp)));
302 break;
303 }
304 }
305
306 /* stop writer by setting s to -1 and give signal in case waiting for data */
307 client->sock = -1;
308 pthread_mutex_lock(&client->replyq->mutex);
309 pthread_cond_signal(&client->replyq->cond);
310 pthread_mutex_unlock(&client->replyq->mutex);
311 debug(DBG_DBG, "tcpserverrd: waiting for writer to end");
312 pthread_join(tcpserverwrth, NULL);
313 debug(DBG_DBG, "tcpserverrd: reader for %s exiting", addr2string(client->addr, tmp, sizeof(tmp)));
314 }
tcpservernew(void * arg)315 void *tcpservernew(void *arg) {
316 int s;
317 struct sockaddr_storage from;
318 socklen_t fromlen = sizeof(from);
319 struct clsrvconf *conf;
320 struct client *client;
321 char tmp[INET6_ADDRSTRLEN];
322
323 s = *(int *)arg;
324 free(arg);
325 if (getpeername(s, (struct sockaddr *)&from, &fromlen)) {
326 debug(DBG_DBG, "tcpservernew: getpeername failed, exiting");
327 goto exit;
328 }
329 debug(DBG_WARN, "tcpservernew: incoming TCP connection from %s", addr2string((struct sockaddr *)&from, tmp, sizeof(tmp)));
330
331 conf = find_clconf(handle, (struct sockaddr *)&from, NULL);
332 if (conf) {
333 client = addclient(conf, 1);
334 if (client) {
335 if(conf->keepalive)
336 enable_keepalive(s);
337 client->sock = s;
338 client->addr = addr_copy((struct sockaddr *)&from);
339 tcpserverrd(client);
340 removeclient(client);
341 } else
342 debug(DBG_WARN, "tcpservernew: failed to create new client instance");
343 } else
344 debug(DBG_WARN, "tcpservernew: ignoring request, no matching TCP client");
345
346 exit:
347 shutdown(s, SHUT_RDWR);
348 close(s);
349 pthread_exit(NULL);
350 }
351
tcplistener(void * arg)352 void *tcplistener(void *arg) {
353 pthread_t tcpserverth;
354 int s, *sp = (int *)arg, *s_arg = NULL;
355 struct sockaddr_storage from;
356 socklen_t fromlen = sizeof(from);
357
358 listen(*sp, 128);
359
360 for (;;) {
361 s = accept(*sp, (struct sockaddr *)&from, &fromlen);
362 if (s < 0) {
363 debug(DBG_WARN, "accept failed");
364 continue;
365 }
366 s_arg = malloc(sizeof(s));
367 if (!s_arg)
368 debugx(1, DBG_ERR, "malloc failed");
369 *s_arg = s;
370 if (pthread_create(&tcpserverth, &pthread_attr, tcpservernew, (void *) s_arg)) {
371 debug(DBG_ERR, "tcplistener: pthread_create failed");
372 free(s_arg);
373 shutdown(s, SHUT_RDWR);
374 close(s);
375 continue;
376 }
377 pthread_detach(tcpserverth);
378 }
379 free(sp);
380 return NULL;
381 }
382 #else
tcpinit(uint8_t h)383 const struct protodefs *tcpinit(uint8_t h) {
384 return NULL;
385 }
386 #endif
387
388 /* Local Variables: */
389 /* c-file-style: "stroustrup" */
390 /* End: */
391