Various changes, see Changelog
[revinetd.git] / server.c
blob5e754dd59b6f4276abec24c69753d162cd5d8ef4
1 /*
2 * server.c
4 * This file is a part of the revinetd project
6 * Revinetd is copyright (c) 2003-2008 by Steven M. Gill
7 * and distributed under the GPL.
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
22 * USA.
24 * */
27 Changelog:
29 p5 - option switched statistics output
30 - statistics.size_sock_queue now also used for controlling
31 priority of handling (##004)
33 p4 - adding "continue"s in the main loop for proxy_sock acceptance
34 and ra_sock acceptance (##001 and ##002)
35 - introducing auto sockq cleanup (by re-requesting a worker connection
36 if necessary) (##003)
37 For this, timeout to the select() call had to be added, and
38 "create_time" member introduced.
39 Avoided CPU overload by a usleep(10000) call near "rc == 0".
40 - prepared: outputting connection statistics
42 2020-06-09 - complete restructuring of the main loop, to obtain
43 (thp) correct behaviour for relay agent connection loss and
44 reconnection.
45 Now, the proxy_sock accept branch is prevented,
46 thus avoiding blocking or idling.
47 [Could optionally shutdown client-facing listen socket
48 upon relay agent's disconnect; not implemented]
49 - slight adjustment of error/info messages
50 - code line rearrangement near "chan_remove(temp)"
51 - verbosity handling adjusted
54 #include "revinetd.h"
55 #include "includes.h"
56 #include "proxy.h"
57 #include "server.h"
58 #include "misc.h"
59 #include "statistics.h"
61 #include "time.h"
63 static const char cvsid[] = "$Id: server.c,v 1.28 2008/08/28 03:24:59 necrotaur Exp $";
65 extern Statistics statistics;
66 time_t next_display_time = 0;
67 #define STATS_INTVL 3
69 // On the client-facing end:
70 #define LISTEN_QUEUE_SIZE 10
72 void
73 cond_print_stats()
75 time_t now = time(NULL);
76 if (now >= next_display_time)
78 printf("%8d\t%8d\t%8d\t%8d\t%8d\t%8d\t%8d\n",
79 statistics.nmb_client_accepts_acc,
80 statistics.nmb_sock_queue_enqueues_acc,
81 statistics.size_sock_queue,
82 statistics.nmb_relay_worker_conns_acc,
83 statistics.nmb_relay_worker_conns_acc - statistics.nmb_relay_worker_conns_cur,
84 statistics.nmb_relay_worker_conns_cur,
85 statistics.nmb_keep_alives_recv);
86 fflush(stdout);
87 next_display_time = now + STATS_INTVL;
91 // Allows "host" to be of format "[xxxx:xxxx:...:xxxx]".
92 int
93 server(char *host,int port, char *host2, int port2)
95 int ra_sock, proxy_sock, tmp_sock, tmp_sock_4or6, bh_sock = -1;
96 fd_set active, read;
97 struct sockaddr_in tmp_sin;
98 struct sockaddr_in6 tmp_sin6;
99 Channels *tmp_chan, *temp;
100 SockQueue *tmp_sq, *sockq = NULL;
101 socklen_t size;
103 struct timeval timeout; // select() timeout spec
104 timeout.tv_sec = 1;
105 timeout.tv_usec = 0;
107 /* Make the sockets. */
108 char host_strp[strlen(host)];
109 int proxy_af = detect_address_family(host, host_strp);
110 if (proxy_af < 0) {
111 fprintf(stderr, "server(): listen-client hostname value is not in valid format: %s\n", host);
112 exit(EXIT_FAILURE);
115 if (conf.verbosity >= VB_NORMAL) printf("I: Binding relay end server socket...\n");
116 ra_sock = make_socket_IPv4(host2, port2);
117 if (conf.verbosity >= VB_NORMAL) printf("I: Binding client end server socket...\n");
118 proxy_sock = (proxy_af == AF_INET6 ? make_socket_IPv6(host_strp, port) : make_socket_IPv4(host, port));
119 register_sock(ra_sock);
120 register_sock(proxy_sock);
122 if (listen(ra_sock, 1) < 0) {
123 perror("listen");
124 exit(EXIT_FAILURE);
126 if (conf.verbosity >= VB_NORMAL)
127 printf("I: Waiting for relay agent on %s:%u\n", host2, port2);
129 FD_ZERO(&active);
130 FD_SET(ra_sock, &active);
132 // Now start to listen (pro-forma) on the client-facing end:
133 if (listen(proxy_sock, LISTEN_QUEUE_SIZE) < 0) {
134 perror("listen");
135 exit(EXIT_FAILURE);
137 // ... but do not actually accept while ra not yet connected:
138 //No: FD_SET(proxy_sock, &active);
140 /* No channels yet. */
141 chan = NULL;
143 reset_stats(&statistics);
145 while (1) {
146 if (conf.verbosity >= VB_DEBUG)
147 cond_print_stats();
149 if (sockq != NULL) { // ##003
150 if (sockq->create_time + 4 < time(NULL)) // if the sockq entry is already 4 secs old, regenerate a worker connection request:
152 if (conf.verbosity >= VB_VERBOSE)
153 printf("I: Extraordinarily requesting new proxy pair from relay agent\n");
154 send_comm_message(bh_sock, RA_TARGET_UP);
155 sockq->create_time = time(NULL);
159 read = active;
160 int rc = select(FD_SETSIZE, &read, NULL, NULL, &timeout);
161 if (rc < 0) {
162 perror("select");
163 exit(EXIT_FAILURE);
165 if (rc == 0) {
166 usleep(10000); /* wait for 10 msecs */
167 continue;
170 // Handle changes on the relay side with priority:
171 if (FD_ISSET(ra_sock, &read)) {
172 /* Connect with relay agent. */
173 size = sizeof(tmp_sin);
174 tmp_sock = accept(ra_sock, (struct sockaddr *)&tmp_sin, &size);
175 if (tmp_sock < 0) {
176 perror("accept");
177 exit(EXIT_FAILURE);
179 register_sock(tmp_sock);
181 if (bh_sock == -1) {
182 if (conf.verbosity >= VB_NORMAL)
183 printf("I: Accepting relay agent from %s:%u\n",
184 inet_ntoa(tmp_sin.sin_addr),
185 ntohs(tmp_sin.sin_port));
186 bh_sock = tmp_sock;
187 FD_SET(bh_sock, &active);
188 // Switch on accepting client connections later:
189 FD_SET(proxy_sock, &active);
190 listen(proxy_sock, LISTEN_QUEUE_SIZE);
191 if (conf.verbosity >= VB_VERBOSE)
192 printf("I: (Re-)Activating waiting for clients on %s:%u\n", host, port);
193 } else {
194 if (sockq == NULL) {
195 if (conf.verbosity >= VB_NORMAL)
196 printf("W: Unrequested connection from another relay agent.\n");
197 // TODO: closing tmp_sock here?
198 } else {
199 // Any connection attempt that is made at the ra_sock
200 // end while the backhaul is standing is understood to
201 // be a data transfer channel established by the relay
202 // upon our own request. Thus, match it to the half-open
203 // connection queue pointed to by sockq:
205 if (conf.verbosity >= VB_VERBOSE)
206 printf("I: New relay agent (worker) connection established from %s:%hu\n",
207 inet_ntoa(tmp_sin.sin_addr),
208 ntohs(tmp_sin.sin_port));
210 tmp_sq = sockq;
212 if (chan == NULL) {
213 chan = chan_add();
214 tmp_chan = chan;
215 } else {
216 tmp_chan = chan;
217 while (tmp_chan->next != NULL)
218 tmp_chan = tmp_chan->next;
219 tmp_chan->next = chan_add();
220 tmp_chan->next->prev = tmp_chan;
221 tmp_chan = tmp_chan->next;
224 tmp_chan->source = tmp_sq->sock;
225 tmp_chan->target = tmp_sock;
226 statistics.nmb_relay_worker_conns_acc++;
227 statistics.nmb_relay_worker_conns_cur++;
229 FD_SET(tmp_chan->source, &active);
230 FD_SET(tmp_chan->target, &active);
232 // Finally remove the processed from the queue of half-open
233 // connections:
234 sockq = tmp_sq->next;
235 free(tmp_sq);
236 statistics.size_sock_queue--;
238 } /* end bh != -1 */
239 continue;
240 // ##001
241 } /* end ra_sock readable */
243 /*----------------------------------------------*/
245 if (FD_ISSET(bh_sock, &read)) {
246 /* We don't expect anything back. This should
247 mean that comm is dropped. relay agent is
248 disconnected so reset state and wait for
249 another relay agent. */
250 /* Slight amend, now we either get an error or a
251 * heart beat... Hopefully this won't break anything. */
252 if (get_comm_message(bh_sock) == SV_HEART_BEAT) {
253 send_comm_message(bh_sock, RA_SERVER_ALIVE);
254 statistics.nmb_keep_alives_recv++;
255 } else {
256 if (conf.verbosity >= VB_NORMAL)
257 printf("W: Relay agent disconnected. Resetting...\n");
258 unregister_sock(bh_sock);
259 FD_CLR(bh_sock, &active);
260 bh_sock = -1;
261 temp = chan;
262 while (temp != NULL) {
263 FD_CLR(temp->source, &active);
264 FD_CLR(temp->target, &active);
265 chan_remove(temp); // deallocates "temp" !!
267 // TODO:
268 // Review: Could make sense to clear the sock queue here as well?
269 // ...
271 // Finally ensure that the proxy_sock accept() branch above
272 // is excluded from running, and avoid clients piling up in
273 // listen queue:
274 FD_CLR(proxy_sock, &active);
275 listen(proxy_sock, 0);
276 if (conf.verbosity >= VB_NORMAL)
277 printf("W: Client-facing port stopping accepting connections.\n");
279 } /* end bh_sock ready */
281 /*----------------------------------------------*/
283 if (FD_ISSET(proxy_sock, &read)) {
284 if (bh_sock == -1) {
285 if (conf.verbosity >= VB_NORMAL)
286 printf("E: Ignoring a client-facing connection until relay agent is connected. (should not occur)\n");
287 usleep(1000000); /* wait for a second */
288 continue; /* Ignore until relay agent is connected. */
289 // In fact, this branch should not occur anymore; the client-side
290 // socket is only to be marked ready if the relay agent is available.
293 /* Accept the client, creating a client-side connection: */
294 size = (proxy_af == AF_INET6 ? sizeof(tmp_sin6) : sizeof(tmp_sin));
295 tmp_sock_4or6 = accept(proxy_sock, (struct sockaddr *)&tmp_sin6,
296 &size);
297 if (tmp_sock_4or6 < 0) {
298 perror("accept");
299 exit(EXIT_FAILURE);
301 register_sock(tmp_sock_4or6);
302 statistics.nmb_client_accepts_acc++;
304 if (conf.verbosity >= VB_VERBOSE)
306 char buf6[128];
307 inet_ntop(proxy_af, &(tmp_sin6.sin6_addr), buf6, 127);
308 printf("I: New client connection detected from %s:%hu\n",
309 //inet_ntoa(tmp_sin6.sin6_addr),
310 buf6,
311 ntohs(tmp_sin6.sin6_port));
314 /* New sockq entry for half open connections. */
315 if (sockq == NULL) {
316 sockq = (SockQueue *)malloc(sizeof(SockQueue));
317 if (sockq == NULL) {
318 perror("malloc");
319 exit(EXIT_FAILURE);
321 memset(sockq, 0, sizeof(SockQueue));
322 tmp_sq = sockq;
323 } else {
324 tmp_sq = sockq;
325 while (tmp_sq->next != NULL)
326 tmp_sq = tmp_sq->next;
327 tmp_sq->next = (SockQueue *)malloc(sizeof(SockQueue));
328 tmp_sq = tmp_sq->next;
329 if (tmp_sq == NULL) {
330 perror("malloc");
331 exit(EXIT_FAILURE);
333 memset(tmp_sq, 0, sizeof(SockQueue));
335 tmp_sq->sock = tmp_sock_4or6;
336 tmp_sq->create_time = time(NULL);
337 statistics.nmb_sock_queue_enqueues_acc++;
338 statistics.size_sock_queue++;
340 /* Now we send a request for a new backhaul channel. */
341 if (conf.verbosity >= VB_VERBOSE)
342 printf("I: Requesting new proxy pair from relay agent\n");
343 send_comm_message(bh_sock, RA_TARGET_UP);
345 if (statistics.size_sock_queue < 20) // ##004
346 continue;
347 // ##002: By adding "continue", client-side connection attempts
348 // are handled with priority, with effect that
349 // first all connections which are desired are accepted
350 // and thus established, and only then afterwards the data
351 // transfer begins. Therefore, do not fall through to proxy()
352 // directly.
353 } /* end proxy_sock ready */
355 /*----------------------------------------------*/
357 // Process possible data which is ready on the worker
358 // connections.
359 proxy(&read, &active);
362 return(0);