separated tcp
authorvenaas <venaas>
Thu, 21 Aug 2008 07:45:34 +0000 (07:45 +0000)
committervenaas <venaas@e88ac4ed-0b26-0410-9574-a7f39faa03bf>
Thu, 21 Aug 2008 07:45:34 +0000 (07:45 +0000)
git-svn-id: https://svn.testnett.uninett.no/radsecproxy/trunk@348 e88ac4ed-0b26-0410-9574-a7f39faa03bf

Makefile
Makefile.am
dtls.c
radsecproxy.c
radsecproxy.h
tcp.c [new file with mode: 0644]
tcp.h [new file with mode: 0644]

index d84eabd..1a28112 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 CFLAGS = -g -Wall -pedantic -pthread
 LDFLAGS = -lssl
-OBJ = util.o debug.o list.o gconfig.o dtls.o radsecproxy.o
+OBJ = util.o debug.o list.o gconfig.o tcp.o dtls.o radsecproxy.o
 
 all: radsecproxy
 
index ad22fc1..6217aaf 100644 (file)
@@ -5,12 +5,14 @@ radsecproxy_SOURCES = radsecproxy.c \
                       util.c \
                       debug.c \
                       list.c \
+                     tcp.c \
                      dtls.c \
                       radsecproxy.h \
                       gconfig.h \
                       debug.h \
                       util.h \
                       list.h \
+                     tcp.h \
                      dtls.h
 
 radsecproxy_CFLAGS  = -g -Wall -pedantic -pthread @SSL_CFLAGS@ @TARGET_CFLAGS@
diff --git a/dtls.c b/dtls.c
index b44a921..5bd7826 100644 (file)
--- a/dtls.c
+++ b/dtls.c
@@ -470,4 +470,3 @@ void *dtlsclientrd(void *arg) {
            free(buf);
     }
 }
-
index 14a28bd..17d3685 100644 (file)
@@ -235,6 +235,10 @@ static int verify_cb(int ok, X509_STORE_CTX *ctx) {
   return ok;
 }
 
+struct addrinfo *getsrcprotores(uint8_t type) {
+    return srcprotores[type];
+}
+
 int resolvepeer(struct clsrvconf *conf, int ai_flags) {
     struct addrinfo hints, *addrinfo, *res;
     char *slash, *s;
@@ -1066,55 +1070,6 @@ int tlsconnect(struct server *server, struct timeval *when, int timeout, char *t
     return 1;
 }
 
-int tcpconnect(struct server *server, struct timeval *when, int timeout, char *text) {
-    struct timeval now;
-    time_t elapsed;
-    
-    debug(DBG_DBG, "tcpconnect: called from %s", text);
-    pthread_mutex_lock(&server->lock);
-    if (when && memcmp(&server->lastconnecttry, when, sizeof(struct timeval))) {
-       /* already reconnected, nothing to do */
-       debug(DBG_DBG, "tcpconnect(%s): seems already reconnected", text);
-       pthread_mutex_unlock(&server->lock);
-       return 1;
-    }
-
-    for (;;) {
-       gettimeofday(&now, NULL);
-       elapsed = now.tv_sec - server->lastconnecttry.tv_sec;
-       if (timeout && server->lastconnecttry.tv_sec && elapsed > timeout) {
-           debug(DBG_DBG, "tcpconnect: timeout");
-           if (server->sock >= 0)
-               close(server->sock);
-           pthread_mutex_unlock(&server->lock);
-           return 0;
-       }
-       if (server->connectionok) {
-           server->connectionok = 0;
-           sleep(2);
-       } else if (elapsed < 1)
-           sleep(2);
-       else if (elapsed < 60) {
-           debug(DBG_INFO, "tcpconnect: sleeping %lds", elapsed);
-           sleep(elapsed);
-       } else if (elapsed < 100000) {
-           debug(DBG_INFO, "tcpconnect: sleeping %ds", 60);
-           sleep(60);
-       } else
-           server->lastconnecttry.tv_sec = now.tv_sec;  /* no sleep at startup */
-       debug(DBG_WARN, "tcpconnect: trying to open TCP connection to %s port %s", server->conf->host, server->conf->port);
-       if (server->sock >= 0)
-           close(server->sock);
-       if ((server->sock = connecttcp(server->conf->addrinfo, srcprotores[RAD_TCP])) >= 0)
-           break;
-       debug(DBG_ERR, "tcpconnect: connecttcp failed");
-    }
-    debug(DBG_WARN, "tcpconnect: TCP connection to %s port %s up", server->conf->host, server->conf->port);
-    gettimeofday(&server->lastconnecttry, NULL);
-    pthread_mutex_unlock(&server->lock);
-    return 1;
-}
-
 /* timeout in seconds, 0 means no timeout (blocking), returns when num bytes have been read, or timeout */
 /* returns 0 on timeout, -1 on error and num if ok */
 int sslreadtimeout(SSL *ssl, unsigned char *buf, int num, int timeout) {
@@ -1194,73 +1149,6 @@ unsigned char *radtlsget(SSL *ssl, int timeout) {
     return rad;
 }
 
-/* timeout in seconds, 0 means no timeout (blocking), returns when num bytes have been read, or timeout */
-/* returns 0 on timeout, -1 on error and num if ok */
-int tcpreadtimeout(int s, unsigned char *buf, int num, int timeout) {
-    int ndesc, cnt, len;
-    fd_set readfds, writefds;
-    struct timeval timer;
-    
-    if (s < 0)
-       return -1;
-    /* make socket non-blocking? */
-    for (len = 0; len < num; len += cnt) {
-       FD_ZERO(&readfds);
-       FD_SET(s, &readfds);
-       writefds = readfds;
-       if (timeout) {
-           timer.tv_sec = timeout;
-           timer.tv_usec = 0;
-       }
-       ndesc = select(s + 1, &readfds, &writefds, NULL, timeout ? &timer : NULL);
-       if (ndesc < 1)
-           return ndesc;
-
-       cnt = read(s, buf + len, num - len);
-       if (cnt <= 0)
-           return -1;
-    }
-    return num;
-}
-
-/* timeout in seconds, 0 means no timeout (blocking) */
-unsigned char *radtcpget(int s, int timeout) {
-    int cnt, len;
-    unsigned char buf[4], *rad;
-
-    for (;;) {
-       cnt = tcpreadtimeout(s, buf, 4, timeout);
-       if (cnt < 1) {
-           debug(DBG_DBG, cnt ? "radtcpget: connection lost" : "radtcpget: timeout");
-           return NULL;
-       }
-
-       len = RADLEN(buf);
-       rad = malloc(len);
-       if (!rad) {
-           debug(DBG_ERR, "radtcpget: malloc failed");
-           continue;
-       }
-       memcpy(rad, buf, 4);
-       
-       cnt = tcpreadtimeout(s, rad + 4, len - 4, timeout);
-       if (cnt < 1) {
-           debug(DBG_DBG, cnt ? "radtcpget: connection lost" : "radtcpget: timeout");
-           free(rad);
-           return NULL;
-       }
-       
-       if (len >= 20)
-           break;
-       
-       free(rad);
-       debug(DBG_WARN, "radtcpget: packet smaller than minimum radius size");
-    }
-    
-    debug(DBG_DBG, "radtcpget: got %d bytes", len);
-    return rad;
-}
-
 int clientradputudp(struct server *server, unsigned char *rad) {
     size_t len;
     struct sockaddr_storage sa;
@@ -1322,25 +1210,6 @@ int clientradputtls(struct server *server, unsigned char *rad) {
     return 1;
 }
 
-int clientradputtcp(struct server *server, unsigned char *rad) {
-    int cnt;
-    size_t len;
-    struct timeval lastconnecttry;
-    struct clsrvconf *conf = server->conf;
-    
-    len = RADLEN(rad);
-    lastconnecttry = server->lastconnecttry;
-    while ((cnt = write(server->sock, rad, len)) <= 0) {
-       debug(DBG_ERR, "clientradputtcp: write error");
-       tcpconnect(server, &lastconnecttry, 0, "clientradputtcp");
-       lastconnecttry = server->lastconnecttry;
-    }
-
-    server->connectionok = 1;
-    debug(DBG_DBG, "clientradputtcp: Sent %d bytes, Radius packet of length %d to TCP peer %s", cnt, len, conf->host);
-    return 1;
-}
-
 int radsign(unsigned char *rad, unsigned char *sec) {
     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
     static unsigned char first = 1;
@@ -2598,27 +2467,6 @@ void *tlsclientrd(void *arg) {
     return NULL;
 }
 
-void *tcpclientrd(void *arg) {
-    struct server *server = (struct server *)arg;
-    unsigned char *buf;
-    struct timeval lastconnecttry;
-    
-    for (;;) {
-       /* yes, lastconnecttry is really necessary */
-       lastconnecttry = server->lastconnecttry;
-       buf = radtcpget(server->sock, 0);
-       if (!buf) {
-           tcpconnect(server, &lastconnecttry, 0, "tcpclientrd");
-           continue;
-       }
-
-       if (!replyh(server, buf))
-           free(buf);
-    }
-    server->clientrdgone = 1;
-    return NULL;
-}
-
 /* code for removing state not finished */
 void *clientwr(void *arg) {
     struct server *server = (struct server *)arg;
@@ -2987,137 +2835,6 @@ void *tlslistener(void *arg) {
     return NULL;
 }
 
-void *tcpserverwr(void *arg) {
-    int cnt;
-    struct client *client = (struct client *)arg;
-    struct queue *replyq;
-    struct reply *reply;
-    
-    debug(DBG_DBG, "tcpserverwr: starting for %s", client->conf->host);
-    replyq = client->replyq;
-    for (;;) {
-       pthread_mutex_lock(&replyq->mutex);
-       while (!list_first(replyq->entries)) {
-           if (client->sock >= 0) {        
-               debug(DBG_DBG, "tcpserverwr: waiting for signal");
-               pthread_cond_wait(&replyq->cond, &replyq->mutex);
-               debug(DBG_DBG, "tcpserverwr: got signal");
-           }
-           if (client->sock < 0) {
-               /* s might have changed while waiting */
-               pthread_mutex_unlock(&replyq->mutex);
-               debug(DBG_DBG, "tcpserverwr: exiting as requested");
-               pthread_exit(NULL);
-           }
-       }
-       reply = (struct reply *)list_shift(replyq->entries);
-       pthread_mutex_unlock(&replyq->mutex);
-       cnt = write(client->sock, reply->buf, RADLEN(reply->buf));
-       if (cnt > 0)
-           debug(DBG_DBG, "tcpserverwr: sent %d bytes, Radius packet of length %d",
-                 cnt, RADLEN(reply->buf));
-       else
-           debug(DBG_ERR, "tcpserverwr: write error for %s", client->conf->host);
-       free(reply->buf);
-       free(reply);
-    }
-}
-
-void tcpserverrd(struct client *client) {
-    struct request rq;
-    pthread_t tcpserverwrth;
-    
-    debug(DBG_DBG, "tcpserverrd: starting for %s", client->conf->host);
-    
-    if (pthread_create(&tcpserverwrth, NULL, tcpserverwr, (void *)client)) {
-       debug(DBG_ERR, "tcpserverrd: pthread_create failed");
-       return;
-    }
-
-    for (;;) {
-       memset(&rq, 0, sizeof(struct request));
-       rq.buf = radtcpget(client->sock, 0);
-       if (!rq.buf) {
-           debug(DBG_ERR, "tcpserverrd: connection from %s lost", client->conf->host);
-           break;
-       }
-       debug(DBG_DBG, "tcpserverrd: got Radius message from %s", client->conf->host);
-       rq.from = client;
-       if (!radsrv(&rq)) {
-           debug(DBG_ERR, "tcpserverrd: message authentication/validation failed, closing connection from %s", client->conf->host);
-           break;
-       }
-    }
-
-    /* stop writer by setting s to -1 and give signal in case waiting for data */
-    client->sock = -1;
-    pthread_mutex_lock(&client->replyq->mutex);
-    pthread_cond_signal(&client->replyq->cond);
-    pthread_mutex_unlock(&client->replyq->mutex);
-    debug(DBG_DBG, "tcpserverrd: waiting for writer to end");
-    pthread_join(tcpserverwrth, NULL);
-    removeclientrqs(client);
-    debug(DBG_DBG, "tcpserverrd: reader for %s exiting", client->conf->host);
-}
-
-void *tcpservernew(void *arg) {
-    int s;
-    struct sockaddr_storage from;
-    size_t fromlen = sizeof(from);
-    struct clsrvconf *conf;
-    struct client *client;
-
-    s = *(int *)arg;
-    if (getpeername(s, (struct sockaddr *)&from, &fromlen)) {
-       debug(DBG_DBG, "tcpservernew: getpeername failed, exiting");
-       goto exit;
-    }
-    debug(DBG_WARN, "tcpservernew: incoming TCP connection from %s", addr2string((struct sockaddr *)&from, fromlen));
-
-    conf = find_conf(RAD_TCP, (struct sockaddr *)&from, clconfs, NULL);
-    if (conf) {
-       client = addclient(conf);
-       if (client) {
-           client->sock = s;
-           tcpserverrd(client);
-           removeclient(client);
-       } else
-           debug(DBG_WARN, "tcpservernew: failed to create new client instance");
-    } else
-       debug(DBG_WARN, "tcpservernew: ignoring request, no matching TCP client");
-
- exit:
-    shutdown(s, SHUT_RDWR);
-    close(s);
-    pthread_exit(NULL);
-}
-
-void *tcplistener(void *arg) {
-    pthread_t tcpserverth;
-    int s, *sp = (int *)arg;
-    struct sockaddr_storage from;
-    size_t fromlen = sizeof(from);
-
-    listen(*sp, 0);
-
-    for (;;) {
-       s = accept(*sp, (struct sockaddr *)&from, &fromlen);
-       if (s < 0) {
-           debug(DBG_WARN, "accept failed");
-           continue;
-       }
-       if (pthread_create(&tcpserverth, NULL, tcpservernew, (void *)&s)) {
-           debug(DBG_ERR, "tcplistener: pthread_create failed");
-           shutdown(s, SHUT_RDWR);
-           close(s);
-           continue;
-       }
-       pthread_detach(tcpserverth);
-    }
-    free(sp);
-    return NULL;
-}
-
 void createlistener(uint8_t type, char *arg) {
     pthread_t th;
     struct clsrvconf *listenres;
index 5ee7a57..c2e9425 100644 (file)
@@ -202,6 +202,7 @@ struct protodefs {
                             sizeof(struct sockaddr_in) : \
                             sizeof(struct sockaddr_in6))
 
+struct addrinfo *getsrcprotores(uint8_t type);
 struct clsrvconf *find_clconf(uint8_t type, struct sockaddr *addr, struct list_node **cur);
 struct clsrvconf *find_srvconf(uint8_t type, struct sockaddr *addr, struct list_node **cur);
 struct client *addclient(struct clsrvconf *conf);
@@ -211,3 +212,4 @@ int radsrv(struct request *rq);
 X509 *verifytlscert(SSL *ssl);
 int verifyconfcert(X509 *cert, struct clsrvconf *conf);
 int replyh(struct server *server, unsigned char *buf);
+int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src);
diff --git a/tcp.c b/tcp.c
new file mode 100644 (file)
index 0000000..e33e4c7
--- /dev/null
+++ b/tcp.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2008 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#ifdef SYS_SOLARIS9
+#include <fcntl.h>
+#endif
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <regex.h>
+#include <pthread.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include "debug.h"
+#include "list.h"
+#include "util.h"
+#include "radsecproxy.h"
+#include "tcp.h"
+
+int tcpconnect(struct server *server, struct timeval *when, int timeout, char *text) {
+    struct timeval now;
+    time_t elapsed;
+    
+    debug(DBG_DBG, "tcpconnect: called from %s", text);
+    pthread_mutex_lock(&server->lock);
+    if (when && memcmp(&server->lastconnecttry, when, sizeof(struct timeval))) {
+       /* already reconnected, nothing to do */
+       debug(DBG_DBG, "tcpconnect(%s): seems already reconnected", text);
+       pthread_mutex_unlock(&server->lock);
+       return 1;
+    }
+
+    for (;;) {
+       gettimeofday(&now, NULL);
+       elapsed = now.tv_sec - server->lastconnecttry.tv_sec;
+       if (timeout && server->lastconnecttry.tv_sec && elapsed > timeout) {
+           debug(DBG_DBG, "tcpconnect: timeout");
+           if (server->sock >= 0)
+               close(server->sock);
+           pthread_mutex_unlock(&server->lock);
+           return 0;
+       }
+       if (server->connectionok) {
+           server->connectionok = 0;
+           sleep(2);
+       } else if (elapsed < 1)
+           sleep(2);
+       else if (elapsed < 60) {
+           debug(DBG_INFO, "tcpconnect: sleeping %lds", elapsed);
+           sleep(elapsed);
+       } else if (elapsed < 100000) {
+           debug(DBG_INFO, "tcpconnect: sleeping %ds", 60);
+           sleep(60);
+       } else
+           server->lastconnecttry.tv_sec = now.tv_sec;  /* no sleep at startup */
+       debug(DBG_WARN, "tcpconnect: trying to open TCP connection to %s port %s", server->conf->host, server->conf->port);
+       if (server->sock >= 0)
+           close(server->sock);
+       if ((server->sock = connecttcp(server->conf->addrinfo, getsrcprotores(RAD_TCP))) >= 0)
+           break;
+       debug(DBG_ERR, "tcpconnect: connecttcp failed");
+    }
+    debug(DBG_WARN, "tcpconnect: TCP connection to %s port %s up", server->conf->host, server->conf->port);
+    gettimeofday(&server->lastconnecttry, NULL);
+    pthread_mutex_unlock(&server->lock);
+    return 1;
+}
+
+/* timeout in seconds, 0 means no timeout (blocking), returns when num bytes have been read, or timeout */
+/* returns 0 on timeout, -1 on error and num if ok */
+int tcpreadtimeout(int s, unsigned char *buf, int num, int timeout) {
+    int ndesc, cnt, len;
+    fd_set readfds, writefds;
+    struct timeval timer;
+    
+    if (s < 0)
+       return -1;
+    /* make socket non-blocking? */
+    for (len = 0; len < num; len += cnt) {
+       FD_ZERO(&readfds);
+       FD_SET(s, &readfds);
+       writefds = readfds;
+       if (timeout) {
+           timer.tv_sec = timeout;
+           timer.tv_usec = 0;
+       }
+       ndesc = select(s + 1, &readfds, &writefds, NULL, timeout ? &timer : NULL);
+       if (ndesc < 1)
+           return ndesc;
+
+       cnt = read(s, buf + len, num - len);
+       if (cnt <= 0)
+           return -1;
+    }
+    return num;
+}
+
+/* timeout in seconds, 0 means no timeout (blocking) */
+unsigned char *radtcpget(int s, int timeout) {
+    int cnt, len;
+    unsigned char buf[4], *rad;
+
+    for (;;) {
+       cnt = tcpreadtimeout(s, buf, 4, timeout);
+       if (cnt < 1) {
+           debug(DBG_DBG, cnt ? "radtcpget: connection lost" : "radtcpget: timeout");
+           return NULL;
+       }
+
+       len = RADLEN(buf);
+       rad = malloc(len);
+       if (!rad) {
+           debug(DBG_ERR, "radtcpget: malloc failed");
+           continue;
+       }
+       memcpy(rad, buf, 4);
+       
+       cnt = tcpreadtimeout(s, rad + 4, len - 4, timeout);
+       if (cnt < 1) {
+           debug(DBG_DBG, cnt ? "radtcpget: connection lost" : "radtcpget: timeout");
+           free(rad);
+           return NULL;
+       }
+       
+       if (len >= 20)
+           break;
+       
+       free(rad);
+       debug(DBG_WARN, "radtcpget: packet smaller than minimum radius size");
+    }
+    
+    debug(DBG_DBG, "radtcpget: got %d bytes", len);
+    return rad;
+}
+
+int clientradputtcp(struct server *server, unsigned char *rad) {
+    int cnt;
+    size_t len;
+    struct timeval lastconnecttry;
+    struct clsrvconf *conf = server->conf;
+    
+    len = RADLEN(rad);
+    lastconnecttry = server->lastconnecttry;
+    while ((cnt = write(server->sock, rad, len)) <= 0) {
+       debug(DBG_ERR, "clientradputtcp: write error");
+       tcpconnect(server, &lastconnecttry, 0, "clientradputtcp");
+       lastconnecttry = server->lastconnecttry;
+    }
+
+    server->connectionok = 1;
+    debug(DBG_DBG, "clientradputtcp: Sent %d bytes, Radius packet of length %d to TCP peer %s", cnt, len, conf->host);
+    return 1;
+}
+
+void *tcpclientrd(void *arg) {
+    struct server *server = (struct server *)arg;
+    unsigned char *buf;
+    struct timeval lastconnecttry;
+    
+    for (;;) {
+       /* yes, lastconnecttry is really necessary */
+       lastconnecttry = server->lastconnecttry;
+       buf = radtcpget(server->sock, 0);
+       if (!buf) {
+           tcpconnect(server, &lastconnecttry, 0, "tcpclientrd");
+           continue;
+       }
+
+       if (!replyh(server, buf))
+           free(buf);
+    }
+    server->clientrdgone = 1;
+    return NULL;
+}
+
+void *tcpserverwr(void *arg) {
+    int cnt;
+    struct client *client = (struct client *)arg;
+    struct queue *replyq;
+    struct reply *reply;
+    
+    debug(DBG_DBG, "tcpserverwr: starting for %s", client->conf->host);
+    replyq = client->replyq;
+    for (;;) {
+       pthread_mutex_lock(&replyq->mutex);
+       while (!list_first(replyq->entries)) {
+           if (client->sock >= 0) {        
+               debug(DBG_DBG, "tcpserverwr: waiting for signal");
+               pthread_cond_wait(&replyq->cond, &replyq->mutex);
+               debug(DBG_DBG, "tcpserverwr: got signal");
+           }
+           if (client->sock < 0) {
+               /* s might have changed while waiting */
+               pthread_mutex_unlock(&replyq->mutex);
+               debug(DBG_DBG, "tcpserverwr: exiting as requested");
+               pthread_exit(NULL);
+           }
+       }
+       reply = (struct reply *)list_shift(replyq->entries);
+       pthread_mutex_unlock(&replyq->mutex);
+       cnt = write(client->sock, reply->buf, RADLEN(reply->buf));
+       if (cnt > 0)
+           debug(DBG_DBG, "tcpserverwr: sent %d bytes, Radius packet of length %d",
+                 cnt, RADLEN(reply->buf));
+       else
+           debug(DBG_ERR, "tcpserverwr: write error for %s", client->conf->host);
+       free(reply->buf);
+       free(reply);
+    }
+}
+
+void tcpserverrd(struct client *client) {
+    struct request rq;
+    pthread_t tcpserverwrth;
+    
+    debug(DBG_DBG, "tcpserverrd: starting for %s", client->conf->host);
+    
+    if (pthread_create(&tcpserverwrth, NULL, tcpserverwr, (void *)client)) {
+       debug(DBG_ERR, "tcpserverrd: pthread_create failed");
+       return;
+    }
+
+    for (;;) {
+       memset(&rq, 0, sizeof(struct request));
+       rq.buf = radtcpget(client->sock, 0);
+       if (!rq.buf) {
+           debug(DBG_ERR, "tcpserverrd: connection from %s lost", client->conf->host);
+           break;
+       }
+       debug(DBG_DBG, "tcpserverrd: got Radius message from %s", client->conf->host);
+       rq.from = client;
+       if (!radsrv(&rq)) {
+           debug(DBG_ERR, "tcpserverrd: message authentication/validation failed, closing connection from %s", client->conf->host);
+           break;
+       }
+    }
+
+    /* stop writer by setting s to -1 and give signal in case waiting for data */
+    client->sock = -1;
+    pthread_mutex_lock(&client->replyq->mutex);
+    pthread_cond_signal(&client->replyq->cond);
+    pthread_mutex_unlock(&client->replyq->mutex);
+    debug(DBG_DBG, "tcpserverrd: waiting for writer to end");
+    pthread_join(tcpserverwrth, NULL);
+    removeclientrqs(client);
+    debug(DBG_DBG, "tcpserverrd: reader for %s exiting", client->conf->host);
+}
+
+void *tcpservernew(void *arg) {
+    int s;
+    struct sockaddr_storage from;
+    size_t fromlen = sizeof(from);
+    struct clsrvconf *conf;
+    struct client *client;
+
+    s = *(int *)arg;
+    if (getpeername(s, (struct sockaddr *)&from, &fromlen)) {
+       debug(DBG_DBG, "tcpservernew: getpeername failed, exiting");
+       goto exit;
+    }
+    debug(DBG_WARN, "tcpservernew: incoming TCP connection from %s", addr2string((struct sockaddr *)&from, fromlen));
+
+    conf = find_clconf(RAD_TCP, (struct sockaddr *)&from, NULL);
+    if (conf) {
+       client = addclient(conf);
+       if (client) {
+           client->sock = s;
+           tcpserverrd(client);
+           removeclient(client);
+       } else
+           debug(DBG_WARN, "tcpservernew: failed to create new client instance");
+    } else
+       debug(DBG_WARN, "tcpservernew: ignoring request, no matching TCP client");
+
+ exit:
+    shutdown(s, SHUT_RDWR);
+    close(s);
+    pthread_exit(NULL);
+}
+
+void *tcplistener(void *arg) {
+    pthread_t tcpserverth;
+    int s, *sp = (int *)arg;
+    struct sockaddr_storage from;
+    size_t fromlen = sizeof(from);
+
+    listen(*sp, 0);
+
+    for (;;) {
+       s = accept(*sp, (struct sockaddr *)&from, &fromlen);
+       if (s < 0) {
+           debug(DBG_WARN, "accept failed");
+           continue;
+       }
+       if (pthread_create(&tcpserverth, NULL, tcpservernew, (void *)&s)) {
+           debug(DBG_ERR, "tcplistener: pthread_create failed");
+           shutdown(s, SHUT_RDWR);
+           close(s);
+           continue;
+       }
+       pthread_detach(tcpserverth);
+    }
+    free(sp);
+    return NULL;
+}
diff --git a/tcp.h b/tcp.h
new file mode 100644 (file)
index 0000000..2864bb4
--- /dev/null
+++ b/tcp.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2008 Stig Venaas <venaas@uninett.no>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+int tcpconnect(struct server *server, struct timeval *when, int timeout, char *text);
+int clientradputtcp(struct server *server, unsigned char *rad);
+void *tcpclientrd(void *arg);
+void *tcplistener(void *arg);