git ssb

0+

cel / sslh



Tree: e492c2808c90ee151362420d6ac0b0d22cb943db

Files: e492c2808c90ee151362420d6ac0b0d22cb943db / sslh-select.c

11703 bytesRaw
1/*
2 sslh-select: mono-processus server
3
4# Copyright (C) 2007-2010 Yves Rutschle
5#
6# This program is free software; you can redistribute it
7# and/or modify it under the terms of the GNU General Public
8# License as published by the Free Software Foundation; either
9# version 2 of the License, or (at your option) any later
10# version.
11#
12# This program is distributed in the hope that it will be
13# useful, but WITHOUT ANY WARRANTY; without even the implied
14# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15# PURPOSE. See the GNU General Public License for more
16# details.
17#
18# The full text for the General Public License is here:
19# http://www.gnu.org/licenses/gpl.html
20
21*/
22
23#define __LINUX__
24
25#include "common.h"
26#include "probe.h"
27
28const char* server_type = "sslh-select";
29
30/* cnx_num_alloc is the number of connection to allocate at once (at start-up,
31 * and then every time we get too many simultaneous connections: e.g. start
32 * with 100 slots, then if we get more than 100 connections allocate another
33 * 100 slots, and so on). We never free up connection structures. We try to
34 * allocate as many structures at once as will fit in one page (which is 102
35 * in sslh 1.9 on Linux on x86)
36 */
37static long cnx_num_alloc;
38
39/* Make the file descriptor non-block */
40int set_nonblock(int fd)
41{
42 int flags;
43
44 flags = fcntl(fd, F_GETFL);
45 CHECK_RES_RETURN(flags, "fcntl");
46
47 flags |= O_NONBLOCK;
48
49 flags = fcntl(fd, F_SETFL, flags);
50 CHECK_RES_RETURN(flags, "fcntl");
51
52 return flags;
53}
54
55int tidy_connection(struct connection *cnx, fd_set *fds, fd_set *fds2)
56{
57 int i;
58
59 for (i = 0; i < 2; i++) {
60 if (cnx->q[i].fd != -1) {
61 if (verbose)
62 fprintf(stderr, "closing fd %d\n", cnx->q[i].fd);
63
64 close(cnx->q[i].fd);
65 FD_CLR(cnx->q[i].fd, fds);
66 FD_CLR(cnx->q[i].fd, fds2);
67 if (cnx->q[i].deferred_data)
68 free(cnx->q[i].deferred_data);
69 }
70 }
71 init_cnx(cnx);
72 return 0;
73}
74
75/* if fd becomes higher than FD_SETSIZE, things won't work so well with FD_SET
76 * and FD_CLR. Need to drop connections if we go above that limit */
77int fd_is_in_range(int fd) {
78 if (fd >= FD_SETSIZE) {
79 log_message(LOG_ERR, "too many open file descriptor to monitor them all -- dropping connection\n");
80 return 0;
81 }
82 return 1;
83}
84
85/* Accepts a connection from the main socket and assigns it to an empty slot.
86 * If no slots are available, allocate another few. If that fails, drop the
87 * connexion */
88int accept_new_connection(int listen_socket, struct connection *cnx[], int* cnx_size)
89{
90 int in_socket, free, i, res;
91 struct connection *new;
92
93 in_socket = accept(listen_socket, 0, 0);
94 CHECK_RES_RETURN(in_socket, "accept");
95
96 if (!fd_is_in_range(in_socket))
97 return -1;
98
99 res = set_nonblock(in_socket);
100 if (res == -1) return -1;
101
102 /* Find an empty slot */
103 for (free = 0; (free < *cnx_size) && ((*cnx)[free].q[0].fd != -1); free++) {
104 /* nothing */
105 }
106 if (free >= *cnx_size) {
107 if (verbose)
108 fprintf(stderr, "buying more slots from the slot machine.\n");
109 new = realloc(*cnx, (*cnx_size + cnx_num_alloc) * sizeof((*cnx)[0]));
110 if (!new) {
111 log_message(LOG_ERR, "unable to realloc -- dropping connection\n");
112 return -1;
113 }
114 *cnx = new;
115 *cnx_size += cnx_num_alloc;
116 for (i = free; i < *cnx_size; i++) {
117 init_cnx(&(*cnx)[i]);
118 }
119 }
120 (*cnx)[free].q[0].fd = in_socket;
121 (*cnx)[free].state = ST_PROBING;
122 (*cnx)[free].probe_timeout = time(NULL) + probing_timeout;
123
124 if (verbose)
125 fprintf(stderr, "accepted fd %d on slot %d\n", in_socket, free);
126
127 return in_socket;
128}
129
130
131/* Connect queue 1 of connection to SSL; returns new file descriptor */
132int connect_queue(struct connection *cnx, fd_set *fds_r, fd_set *fds_w)
133{
134 struct queue *q = &cnx->q[1];
135
136 q->fd = connect_addr(cnx, cnx->q[0].fd);
137 if ((q->fd != -1) && fd_is_in_range(q->fd)) {
138 log_connection(cnx);
139 set_nonblock(q->fd);
140 flush_deferred(q);
141 if (q->deferred_data) {
142 FD_SET(q->fd, fds_w);
143 } else {
144 FD_SET(q->fd, fds_r);
145 }
146 return q->fd;
147 } else {
148 tidy_connection(cnx, fds_r, fds_w);
149 return -1;
150 }
151}
152
153/* shovels data from active fd to the other
154 returns after one socket closed or operation would block
155 */
156void shovel(struct connection *cnx, int active_fd,
157 fd_set *fds_r, fd_set *fds_w)
158{
159 struct queue *read_q, *write_q;
160
161 read_q = &cnx->q[active_fd];
162 write_q = &cnx->q[1-active_fd];
163
164 if (verbose)
165 fprintf(stderr, "activity on fd%d\n", read_q->fd);
166
167 switch(fd2fd(write_q, read_q)) {
168 case -1:
169 case FD_CNXCLOSED:
170 tidy_connection(cnx, fds_r, fds_w);
171 break;
172
173 case FD_STALLED:
174 FD_SET(write_q->fd, fds_w);
175 FD_CLR(read_q->fd, fds_r);
176 break;
177
178 default: /* Nothing */
179 break;
180 }
181}
182
183/* returns true if specified fd is initialised and present in fd_set */
184int is_fd_active(int fd, fd_set* set)
185{
186 if (fd == -1) return 0;
187 return FD_ISSET(fd, set);
188}
189
190/* Main loop: the idea is as follow:
191 * - fds_r and fds_w contain the file descriptors to monitor in read and write
192 * - When a file descriptor goes off, process it: read from it, write the data
193 * to its corresponding pair.
194 * - When a file descriptor blocks when writing, remove the read fd from fds_r,
195 * move the data to a deferred buffer, and add the write fd to fds_w. Defered
196 * buffer is allocated dynamically.
197 * - When we can write to a file descriptor that has deferred data, we try to
198 * write as much as we can. Once all data is written, remove the fd from fds_w
199 * and add its corresponding pair to fds_r, free the buffer.
200 *
201 * That way, each pair of file descriptor (read from one, write to the other)
202 * is monitored either for read or for write, but never for both.
203 */
204void main_loop(int listen_sockets[], int num_addr_listen)
205{
206 fd_set fds_r, fds_w; /* reference fd sets (used to init the next 2) */
207 fd_set readfds, writefds; /* working read and write fd sets */
208 struct timeval tv;
209 int max_fd, in_socket, i, j, res;
210 struct connection *cnx;
211 int num_cnx; /* Number of connections in *cnx */
212 int num_probing = 0; /* Number of connections currently probing
213 * We use this to know if we need to time out of
214 * select() */
215
216 FD_ZERO(&fds_r);
217 FD_ZERO(&fds_w);
218
219 for (i = 0; i < num_addr_listen; i++) {
220 FD_SET(listen_sockets[i], &fds_r);
221 set_nonblock(listen_sockets[i]);
222 }
223 max_fd = listen_sockets[num_addr_listen-1] + 1;
224
225 cnx_num_alloc = getpagesize() / sizeof(struct connection);
226
227 num_cnx = cnx_num_alloc; /* Start with a set pool of slots */
228 cnx = malloc(num_cnx * sizeof(struct connection));
229 for (i = 0; i < num_cnx; i++)
230 init_cnx(&cnx[i]);
231
232 while (1)
233 {
234 memset(&tv, 0, sizeof(tv));
235 tv.tv_sec = probing_timeout;
236
237 memcpy(&readfds, &fds_r, sizeof(readfds));
238 memcpy(&writefds, &fds_w, sizeof(writefds));
239
240 if (verbose)
241 fprintf(stderr, "selecting... max_fd=%d num_probing=%d\n", max_fd, num_probing);
242 res = select(max_fd, &readfds, &writefds, NULL, num_probing ? &tv : NULL);
243 if (res < 0)
244 perror("select");
245
246
247 /* Check main socket for new connections */
248 for (i = 0; i < num_addr_listen; i++) {
249 if (FD_ISSET(listen_sockets[i], &readfds)) {
250 in_socket = accept_new_connection(listen_sockets[i], &cnx, &num_cnx);
251 if (in_socket != -1)
252 num_probing++;
253
254 if (in_socket > 0) {
255 FD_SET(in_socket, &fds_r);
256 if (in_socket >= max_fd)
257 max_fd = in_socket + 1;;
258 }
259 FD_CLR(listen_sockets[i], &readfds);
260 }
261 }
262
263 /* Check all sockets for write activity */
264 for (i = 0; i < num_cnx; i++) {
265 if (cnx[i].q[0].fd != -1) {
266 for (j = 0; j < 2; j++) {
267 if (is_fd_active(cnx[i].q[j].fd, &writefds)) {
268 res = flush_deferred(&cnx[i].q[j]);
269 if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) {
270 if (cnx[i].state == ST_PROBING) num_probing--;
271 tidy_connection(&cnx[i], &fds_r, &fds_w);
272 if (verbose)
273 fprintf(stderr, "closed slot %d\n", i);
274 } else {
275 /* If no deferred data is left, stop monitoring the fd
276 * for write, and restart monitoring the other one for reads*/
277 if (!cnx[i].q[j].deferred_data_size) {
278 FD_CLR(cnx[i].q[j].fd, &fds_w);
279 FD_SET(cnx[i].q[1-j].fd, &fds_r);
280 }
281 }
282 }
283 }
284 }
285 }
286
287 /* Check all sockets for read activity */
288 for (i = 0; i < num_cnx; i++) {
289 for (j = 0; j < 2; j++) {
290 if (is_fd_active(cnx[i].q[j].fd, &readfds) ||
291 ((cnx[i].state == ST_PROBING) && (cnx[i].probe_timeout < time(NULL)))) {
292 if (verbose)
293 fprintf(stderr, "processing fd%d slot %d\n", j, i);
294
295 switch (cnx[i].state) {
296
297 case ST_PROBING:
298 if (j == 1) {
299 fprintf(stderr, "Activity on fd2 while probing, impossible\n");
300 dump_connection(&cnx[i]);
301 exit(1);
302 }
303
304 /* If timed out it's SSH, otherwise the client sent
305 * data so probe the protocol */
306 if ((cnx[i].probe_timeout < time(NULL))) {
307 cnx[i].proto = timeout_protocol();
308 } else {
309 res = probe_client_protocol(&cnx[i]);
310 if (res == PROBE_AGAIN)
311 continue;
312 }
313
314 num_probing--;
315 cnx[i].state = ST_SHOVELING;
316
317 /* libwrap check if required for this protocol */
318 if (cnx[i].proto->service &&
319 check_access_rights(in_socket, cnx[i].proto->service)) {
320 tidy_connection(&cnx[i], &fds_r, &fds_w);
321 res = -1;
322 } else {
323 res = connect_queue(&cnx[i], &fds_r, &fds_w);
324 }
325
326 if (res >= max_fd)
327 max_fd = res + 1;;
328 break;
329
330 case ST_SHOVELING:
331 shovel(&cnx[i], j, &fds_r, &fds_w);
332 break;
333
334 default: /* illegal */
335 log_message(LOG_ERR, "Illegal connection state %d\n", cnx[i].state);
336 exit(1);
337 }
338 }
339 }
340 }
341 }
342}
343
344
345void start_shoveler(int listen_socket) {
346 fprintf(stderr, "inetd mode is not supported in select mode\n");
347 exit(1);
348}
349
350
351/* The actual main is in common.c: it's the same for both version of
352 * the server
353 */
354
355
356

Built with git-ssb-web