aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-04-17 13:05:42 +0300
committerSergey Poznyakoff <gray@gnu.org>2020-04-17 13:50:49 +0300
commit7a1985a0f05564fd7c381e828ff134836f3e3253 (patch)
tree737659273f526bcf9e31cf5efb257b718c6abfae
parent1047ca368eaf1c0f1cda21c9b0ab3c212a3d9379 (diff)
downloadwydawca-7a1985a0f05564fd7c381e828ff134836f3e3253.tar.gz
wydawca-7a1985a0f05564fd7c381e828ff134836f3e3253.tar.bz2
Limit number of simultaneous tcpmux connections. Impose idle connection timeouts.
* src/config.c: New statements: max-connections and idle-timeout * src/net.c (max_connections,idle_timeout): New globals. (handle_connection): Take single FILE* as argument. (connection_start,connection_stop): New functions. (wy_thr_tcpmux,wy_thr_connection_watcher): New functions. (wy_thr_listen): Start wy_thr_connection_watcher thread if tcpmux listener is enabled. Start a separate thread to process each incoming connection. * src/wydawca.h (max_connections,idle_timeout): New externs. (wy_thr_tcpmux,wy_thr_connection_watcher): New protos.
-rw-r--r--src/config.c8
-rw-r--r--src/net.c195
-rw-r--r--src/triplet.c1
-rw-r--r--src/wydawca.c1
-rw-r--r--src/wydawca.h4
5 files changed, 185 insertions, 24 deletions
diff --git a/src/config.c b/src/config.c
index 7d0e760..a7d4312 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1489,7 +1489,13 @@ static struct grecs_keyword wydawca_kw[] = {
1489 { "listen", N_("socket"), 1489 { "listen", N_("socket"),
1490 N_("Listen on this address"), 1490 N_("Listen on this address"),
1491 grecs_type_sockaddr, GRECS_DFLT, &listen_sockaddr, }, 1491 grecs_type_sockaddr, GRECS_DFLT, &listen_sockaddr, },
1492 1492 { "max-connections", NULL,
1493 N_("maximum allowed number of simultaneous connections"),
1494 grecs_type_size, GRECS_DFLT, &max_connections },
1495 { "idle-timeout", N_("interval"),
1496 N_("Idle timeout for a TXPMUX connection"),
1497 grecs_type_string, GRECS_CONST, &idle_timeout, 0, cb_interval },
1498
1493#ifdef WITH_LIBWRAP 1499#ifdef WITH_LIBWRAP
1494 { "tcp-wrapper", NULL, 1500 { "tcp-wrapper", NULL,
1495 N_("Configure TCP wrappers"), 1501 N_("Configure TCP wrappers"),
diff --git a/src/net.c b/src/net.c
index 3f8d291..f0869dc 100644
--- a/src/net.c
+++ b/src/net.c
@@ -16,6 +16,10 @@
16 16
17#include "wydawca.h" 17#include "wydawca.h"
18 18
19struct grecs_sockaddr listen_sockaddr;
20size_t max_connections = 16;
21time_t idle_timeout = 5;
22
19static int 23static int
20open_listener() 24open_listener()
21{ 25{
@@ -80,39 +84,156 @@ trim_crlf(char *s)
80 s[--len] = 0; 84 s[--len] = 0;
81 } 85 }
82} 86}
87
88struct wydawca_connection {
89 struct timespec ts;
90 pthread_t tid;
91 FILE *fp;
92 struct wydawca_connection *next, *prev;
93};
83 94
84void 95struct wydawca_connection_queue {
85handle_connection(FILE * in, FILE * out) 96 struct wydawca_connection *head, *tail;
97};
98
99static inline void
100wydawca_connection_enqueue(struct wydawca_connection_queue *q,
101 struct wydawca_connection *conn)
102{
103 conn->next = NULL;
104 conn->prev = q->tail;
105 if (q->tail)
106 q->tail->next = conn;
107 else
108 q->head = conn;
109 q->tail = conn;
110}
111
112static inline void
113wydawca_connection_dequeue(struct wydawca_connection_queue *q,
114 struct wydawca_connection *conn)
115{
116 if (conn->prev)
117 conn->prev->next = conn->next;
118 else
119 q->head = conn->next;
120 if (conn->next)
121 conn->next->prev = conn->prev;
122 else
123 q->tail = conn->prev;
124 conn->prev = conn->next = NULL;
125}
126
127static struct wydawca_connection *conn_table;
128static struct wydawca_connection_queue conn_avail, conn_idle;
129static pthread_mutex_t conn_mutex = PTHREAD_MUTEX_INITIALIZER;
130static pthread_cond_t conn_cond = PTHREAD_COND_INITIALIZER;
131
132static void
133conn_table_init(void)
134{
135 size_t i;
136
137 conn_table = grecs_calloc(max_connections, sizeof(conn_table[0]));
138 for (i = 1; i < max_connections; i++) {
139 wydawca_connection_enqueue(&conn_avail, &conn_table[i]);
140 }
141}
142
143static void
144connection_start(int fd)
145{
146 struct wydawca_connection *conn;
147
148 pthread_mutex_lock(&conn_mutex);
149 if (conn_avail.head) {
150 conn = conn_avail.head;
151 wydawca_connection_dequeue(&conn_avail, conn);
152 clock_gettime(CLOCK_REALTIME, &conn->ts);
153 conn->ts.tv_sec += idle_timeout;
154 conn->fp = fdopen(fd, "w+");
155 pthread_create(&conn->tid, NULL, wy_thr_tcpmux, conn);
156 wydawca_connection_enqueue(&conn_idle, conn);
157 pthread_cond_broadcast(&conn_cond);
158 } else {
159 wy_log(LOG_ERR, "connection table is full");
160 close(fd);
161 }
162 pthread_mutex_unlock(&conn_mutex);
163}
164
165static void
166connection_unidle(struct wydawca_connection *conn)
167{
168 pthread_mutex_lock(&conn_mutex);
169 fclose(conn->fp);
170 conn->fp = NULL;
171 wydawca_connection_dequeue(&conn_idle, conn);
172 pthread_cond_broadcast(&conn_cond);
173 pthread_mutex_unlock(&conn_mutex);
174}
175
176static void
177connection_stop(struct wydawca_connection *conn)
178{
179 pthread_mutex_lock(&conn_mutex);
180 if (conn->fp) {
181 fclose(conn->fp);
182 conn->fp = NULL;
183 wydawca_connection_dequeue(&conn_idle, conn);
184 pthread_cond_broadcast(&conn_cond);
185 }
186 wydawca_connection_enqueue(&conn_avail, conn);
187 pthread_mutex_unlock(&conn_mutex);
188}
189
190static inline int
191timespec_cmp(struct timespec const *a, struct timespec const *b)
192{
193 if (a->tv_sec > b->tv_sec)
194 return 1;
195 if (a->tv_sec < b->tv_sec)
196 return -1;
197 if (a->tv_nsec > b->tv_nsec)
198 return 1;
199 if (a->tv_nsec < b->tv_nsec)
200 return -1;
201 return 0;
202}
203
204static void
205handle_connection(struct wydawca_connection *conn)
86{ 206{
87 char *buf = NULL; 207 char *buf = NULL;
88 size_t buflen = 0; 208 size_t buflen = 0;
89 struct spool *spool; 209 struct spool *spool;
90 char *p; 210 char *p;
91 211
92 if (grecs_getline(&buf, &buflen, in) <= 0) 212 if (grecs_getline(&buf, &buflen, conn->fp) <= 0)
93 return; 213 return;
94 trim_crlf(buf); 214 trim_crlf(buf);
95 wy_debug(1, ("recv: %s", buf)); 215 wy_debug(1, ("recv: %s", buf));
96 spool = wydawca_find_spool(buf); 216 spool = wydawca_find_spool(buf);
97 if (!spool) { 217 if (!spool) {
98 if (all_spool_aliases && grecs_list_locate(all_spool_aliases, buf)) 218 if (all_spool_aliases && grecs_list_locate(all_spool_aliases, buf))
99 fprintf(out, "+ OK, all spools\r\n"); 219 fprintf(conn->fp, "+ OK, all spools\r\n");
100 else { 220 else {
101 fprintf(out, "- Unknown service name\r\n"); 221 fprintf(conn->fp, "- Unknown service name\r\n");
102 free(buf); 222 free(buf);
103 return; 223 return;
104 } 224 }
105 } else if (spool->url) 225 } else if (spool->url)
106 fprintf(out, "+ OK, URL %s\r\n", spool->url); 226 fprintf(conn->fp, "+ OK, URL %s\r\n", spool->url);
107 else 227 else
108 fprintf(out, "+ OK, spool %s\r\n", spool->tag); 228 fprintf(conn->fp, "+ OK, spool %s\r\n", spool->tag);
109 229
110 if (grecs_getline(&buf, &buflen, in) < 0) { 230 if (grecs_getline(&buf, &buflen, conn->fp) < 0) {
111 wy_log(LOG_ERR, "protocol error"); 231 wy_log(LOG_ERR, "protocol error");
112 free(buf); 232 free(buf);
113 return; 233 return;
114 } 234 }
115 235 connection_unidle(conn);
236
116 trim_crlf(buf); 237 trim_crlf(buf);
117 wy_debug(1, ("recv: %s", buf)); 238 wy_debug(1, ("recv: %s", buf));
118 239
@@ -135,6 +256,42 @@ handle_connection(FILE * in, FILE * out)
135 free(buf); 256 free(buf);
136} 257}
137 258
259void *
260wy_thr_tcpmux(void *ptr)
261{
262 struct wydawca_connection *conn = ptr;
263 pthread_cleanup_push((void (*)(void*))connection_stop, conn);
264 setlinebuf(conn->fp);
265 handle_connection(conn);
266 pthread_cleanup_pop(1);
267 return NULL;
268}
269
270void *
271wy_thr_connection_watcher(void *ptr)
272{
273 pthread_mutex_lock(&conn_mutex);
274 while (1) {
275 if (conn_idle.head) {
276 struct timespec ts;
277
278 pthread_cond_timedwait(&conn_cond, &conn_mutex,
279 &conn_idle.head->ts);