git ssb

0+

cel / sslh



Tree: ae008179f033c8409c69b13787a539351bace626

Files: ae008179f033c8409c69b13787a539351bace626 / sslh-select.c

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

Built with git-ssb-web