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,
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
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
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
59 #include "statistics.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;
69 // On the client-facing end:
70 #define LISTEN_QUEUE_SIZE 10
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
);
87 next_display_time
= now
+ STATS_INTVL
;
91 // Allows "host" to be of format "[xxxx:xxxx:...:xxxx]".
93 server(char *host
,int port
, char *host2
, int port2
)
95 int ra_sock
, proxy_sock
, tmp_sock
, tmp_sock_4or6
, bh_sock
= -1;
97 struct sockaddr_in tmp_sin
;
98 struct sockaddr_in6 tmp_sin6
;
99 Channels
*tmp_chan
, *temp
;
100 SockQueue
*tmp_sq
, *sockq
= NULL
;
103 struct timeval timeout
; // select() timeout spec
107 /* Make the sockets. */
108 char host_strp
[strlen(host
)];
109 int proxy_af
= detect_address_family(host
, host_strp
);
111 fprintf(stderr
, "server(): listen-client hostname value is not in valid format: %s\n", host
);
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) {
126 if (conf
.verbosity
>= VB_NORMAL
)
127 printf("I: Waiting for relay agent on %s:%u\n", host2
, port2
);
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) {
137 // ... but do not actually accept while ra not yet connected:
138 //No: FD_SET(proxy_sock, &active);
140 /* No channels yet. */
143 reset_stats(&statistics
);
146 if (conf
.verbosity
>= VB_DEBUG
)
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
);
160 int rc
= select(FD_SETSIZE
, &read
, NULL
, NULL
, &timeout
);
166 usleep(10000); /* wait for 10 msecs */
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
);
179 register_sock(tmp_sock
);
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
));
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
);
195 if (conf
.verbosity
>= VB_NORMAL
)
196 printf("W: Unrequested connection from another relay agent.\n");
197 // TODO: closing tmp_sock here?
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
));
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
234 sockq
= tmp_sq
->next
;
236 statistics
.size_sock_queue
--;
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
++;
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
);
262 while (temp
!= NULL
) {
263 FD_CLR(temp
->source
, &active
);
264 FD_CLR(temp
->target
, &active
);
265 chan_remove(temp
); // deallocates "temp" !!
268 // Review: Could make sense to clear the sock queue here as well?
271 // Finally ensure that the proxy_sock accept() branch above
272 // is excluded from running, and avoid clients piling up in
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
)) {
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
,
297 if (tmp_sock_4or6
< 0) {
301 register_sock(tmp_sock_4or6
);
302 statistics
.nmb_client_accepts_acc
++;
304 if (conf
.verbosity
>= VB_VERBOSE
)
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),
311 ntohs(tmp_sin6
.sin6_port
));
314 /* New sockq entry for half open connections. */
316 sockq
= (SockQueue
*)malloc(sizeof(SockQueue
));
321 memset(sockq
, 0, sizeof(SockQueue
));
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
) {
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
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()
353 } /* end proxy_sock ready */
355 /*----------------------------------------------*/
357 // Process possible data which is ready on the worker
359 proxy(&read
, &active
);