git ssb

0+

cel / sslh



Tree: 9bcb2cdd7a920ebc78b59d0b5797d678424aa93a

Files: 9bcb2cdd7a920ebc78b59d0b5797d678424aa93a / sslh-select.c

11364 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].defered_data)
68 free(cnx->q[i].defered_data);
69 }
70 }
71 init_cnx(cnx);
72 return 0;
73}
74
75/* Accepts a connection from the main socket and assigns it to an empty slot.
76 * If no slots are available, allocate another few. If that fails, drop the
77 * connexion */
78int accept_new_connection(int listen_socket, struct connection *cnx[], int* cnx_size)
79{
80 int in_socket, free, i, res;
81 struct connection *new;
82
83 in_socket = accept(listen_socket, 0, 0);
84 CHECK_RES_RETURN(in_socket, "accept");
85
86 res = set_nonblock(in_socket);
87 if (res == -1) return -1;
88
89 /* Find an empty slot */
90 for (free = 0; (free < *cnx_size) && ((*cnx)[free].q[0].fd != -1); free++) {
91 /* nothing */
92 }
93 if (free >= *cnx_size) {
94 if (verbose)
95 fprintf(stderr, "buying more slots from the slot machine.\n");
96 new = realloc(*cnx, (*cnx_size + cnx_num_alloc) * sizeof((*cnx)[0]));
97 if (!new) {
98 log_message(LOG_ERR, "unable to realloc -- dropping connection\n");
99 return -1;
100 }
101 *cnx = new;
102 *cnx_size += cnx_num_alloc;
103 for (i = free; i < *cnx_size; i++) {
104 init_cnx(&(*cnx)[i]);
105 }
106 }
107 (*cnx)[free].q[0].fd = in_socket;
108 (*cnx)[free].state = ST_PROBING;
109 (*cnx)[free].probe_timeout = time(NULL) + probing_timeout;
110
111 if (verbose)
112 fprintf(stderr, "accepted fd %d on slot %d\n", in_socket, free);
113
114 return in_socket;
115}
116
117
118/* Connect queue 1 of connection to SSL; returns new file descriptor */
119int connect_queue(struct connection *cnx, struct addrinfo *addr,
120 const char* cnx_name,
121 fd_set *fds_r, fd_set *fds_w)
122{
123 struct queue *q = &cnx->q[1];
124
125 q->fd = connect_addr(addr, cnx_name);
126 if (q->fd != -1) {
127 log_connection(cnx);
128 set_nonblock(q->fd);
129 flush_defered(q);
130 if (q->defered_data) {
131 FD_SET(q->fd, fds_w);
132 } else {
133 FD_SET(q->fd, fds_r);
134 }
135 return q->fd;
136 } else {
137 tidy_connection(cnx, fds_r, fds_w);
138 return -1;
139 }
140}
141
142/* shovels data from active fd to the other
143 returns after one socket closed or operation would block
144 */
145void shovel(struct connection *cnx, int active_fd,
146 fd_set *fds_r, fd_set *fds_w)
147{
148 struct queue *read_q, *write_q;
149
150 read_q = &cnx->q[active_fd];
151 write_q = &cnx->q[1-active_fd];
152
153 if (verbose)
154 fprintf(stderr, "activity on fd%d\n", read_q->fd);
155
156 switch(fd2fd(write_q, read_q)) {
157 case -1:
158 case FD_CNXCLOSED:
159 tidy_connection(cnx, fds_r, fds_w);
160 break;
161
162 case FD_STALLED:
163 FD_SET(write_q->fd, fds_w);
164 FD_CLR(read_q->fd, fds_r);
165 break;
166
167 default: /* Nothing */
168 break;
169 }
170}
171
172/* returns true if specified fd is initialised and present in fd_set */
173int is_fd_active(int fd, fd_set* set)
174{
175 if (fd == -1) return 0;
176 return FD_ISSET(fd, set);
177}
178
179/* Main loop: the idea is as follow:
180 * - fds_r and fds_w contain the file descritors to monitor in read and write
181 * - When a file descriptor goes off, process it: read from it, write the data
182 * to its corresponding pair.
183 * - When a file descriptor blocks when writing, remove the read fd from fds_r,
184 * move the data to a defered buffer, and add the write fd to fds_w. Defered
185 * buffer is allocated dynamically.
186 * - When we can write to a file descriptor that has defered data, we try to
187 * write as much as we can. Once all data is written, remove the fd from fds_w
188 * and add its corresponding pair to fds_r, free the buffer.
189 *
190 * That way, each pair of file descriptor (read from one, write to the other)
191 * is monitored either for read or for write, but never for both.
192 */
193void main_loop(int listen_sockets[], int num_addr_listen)
194{
195 fd_set fds_r, fds_w; /* reference fd sets (used to init the next 2) */
196 fd_set readfds, writefds; /* working read and write fd sets */
197 struct timeval tv;
198 int max_fd, in_socket, i, j, res;
199 struct connection *cnx;
200 struct proto *prot;
201 int num_cnx; /* Number of connections in *cnx */
202 int num_probing = 0; /* Number of connections currently probing
203 * We use this to know if we need to time out of
204 * select() */
205
206 FD_ZERO(&fds_r);
207 FD_ZERO(&fds_w);
208
209 for (i = 0; i < num_addr_listen; i++) {
210 FD_SET(listen_sockets[i], &fds_r);
211 set_nonblock(listen_sockets[i]);
212 }
213 max_fd = listen_sockets[num_addr_listen-1] + 1;
214
215 cnx_num_alloc = getpagesize() / sizeof(struct connection);
216
217 num_cnx = cnx_num_alloc; /* Start with a set pool of slots */
218 cnx = malloc(num_cnx * sizeof(struct connection));
219 for (i = 0; i < num_cnx; i++)
220 init_cnx(&cnx[i]);
221
222 while (1)
223 {
224 memset(&tv, 0, sizeof(tv));
225 tv.tv_sec = probing_timeout;
226
227 memcpy(&readfds, &fds_r, sizeof(readfds));
228 memcpy(&writefds, &fds_w, sizeof(writefds));
229
230 if (verbose)
231 fprintf(stderr, "selecting... max_fd=%d num_probing=%d\n", max_fd, num_probing);
232 res = select(max_fd, &readfds, &writefds, NULL, num_probing ? &tv : NULL);
233 if (res < 0)
234 perror("select");
235
236
237 /* Check main socket for new connections */
238 for (i = 0; i < num_addr_listen; i++) {
239 if (FD_ISSET(listen_sockets[i], &readfds)) {
240 in_socket = accept_new_connection(listen_sockets[i], &cnx, &num_cnx);
241 if (in_socket != -1)
242 num_probing++;
243
244 if (in_socket > 0) {
245 FD_SET(in_socket, &fds_r);
246 if (in_socket >= max_fd)
247 max_fd = in_socket + 1;;
248 }
249 FD_CLR(listen_sockets[i], &readfds);
250 }
251 }
252
253 /* Check all sockets for write activity */
254 for (i = 0; i < num_cnx; i++) {
255 if (cnx[i].q[0].fd != -1) {
256 for (j = 0; j < 2; j++) {
257 if (is_fd_active(cnx[i].q[j].fd, &writefds)) {
258 res = flush_defered(&cnx[i].q[j]);
259 if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) {
260 if (cnx[i].state == ST_PROBING) num_probing--;
261 tidy_connection(&cnx[i], &fds_r, &fds_w);
262 if (verbose)
263 fprintf(stderr, "closed slot %d\n", i);
264 }
265 /* If no defered data is left, stop monitoring the fd
266 * for write, and restart monitoring the other one for reads*/
267 if (!cnx[i].q[j].defered_data_size) {
268 FD_CLR(cnx[i].q[j].fd, &fds_w);
269 FD_SET(cnx[i].q[1-j].fd, &fds_r);
270 }
271 }
272 }
273 }
274 }
275
276 /* Check all sockets for read activity */
277 for (i = 0; i < num_cnx; i++) {
278 for (j = 0; j < 2; j++) {
279 if (is_fd_active(cnx[i].q[j].fd, &readfds) ||
280 ((cnx[i].state == ST_PROBING) && (cnx[i].probe_timeout < time(NULL)))) {
281 if (verbose)
282 fprintf(stderr, "processing fd%d slot %d\n", j, i);
283
284 switch (cnx[i].state) {
285
286 case ST_PROBING:
287 if (j == 1) {
288 fprintf(stderr, "Activity on fd2 while probing, impossible\n");
289 dump_connection(&cnx[i]);
290 exit(1);
291 }
292 num_probing--;
293 cnx[i].state = ST_SHOVELING;
294
295 /* If timed out it's SSH, otherwise the client sent
296 * data so probe the protocol */
297 if ((cnx[i].probe_timeout < time(NULL))) {
298 prot = timeout_protocol();
299 } else {
300 prot = probe_client_protocol(&cnx[i]);
301 }
302
303 /* libwrap check if required for this protocol */
304 if (prot->service &&
305 check_access_rights(in_socket, prot->service)) {
306 tidy_connection(&cnx[i], &fds_r, &fds_w);
307 res = -1;
308 } else {
309 res = connect_queue(&cnx[i],
310 prot->saddr,
311 prot->description,
312 &fds_r, &fds_w);
313 }
314
315 if (res >= max_fd)
316 max_fd = res + 1;;
317 break;
318
319 case ST_SHOVELING:
320 shovel(&cnx[i], j, &fds_r, &fds_w);
321 break;
322
323 default: /* illegal */
324 log_message(LOG_ERR, "Illegal connection state %d\n", cnx[i].state);
325 exit(1);
326 }
327 }
328 }
329 }
330 }
331}
332
333
334void start_shoveler(int listen_socket) {
335 fprintf(stderr, "inetd mode is not supported in select mode\n");
336 exit(1);
337}
338
339
340/* The actual main is in common.c: it's the same for both version of
341 * the server
342 */
343
344
345

Built with git-ssb-web