Files: 718fe0e2e9f339a022d9bc13285017fdd76a32e1 / sslh-fork.c
4738 bytesRaw
1 | /* |
2 | sslh-fork: forking server |
3 | |
4 | # Copyright (C) 2007-2012 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 | |
24 | |
25 | |
26 | const char* server_type = "sslh-fork"; |
27 | |
28 | |
29 | |
30 | /* shovels data from one fd to the other and vice-versa |
31 | returns after one socket closed |
32 | */ |
33 | int shovel(struct connection *cnx) |
34 | { |
35 | fd_set fds; |
36 | int res, i; |
37 | int max_fd = MAX(cnx->q[0].fd, cnx->q[1].fd) + 1; |
38 | |
39 | FD_ZERO(&fds); |
40 | while (1) { |
41 | FD_SET(cnx->q[0].fd, &fds); |
42 | FD_SET(cnx->q[1].fd, &fds); |
43 | |
44 | res = select( |
45 | max_fd, |
46 | &fds, |
47 | NULL, |
48 | NULL, |
49 | NULL |
50 | ); |
51 | CHECK_RES_DIE(res, "select"); |
52 | |
53 | for (i = 0; i < 2; i++) { |
54 | if (FD_ISSET(cnx->q[i].fd, &fds)) { |
55 | res = fd2fd(&cnx->q[1-i], &cnx->q[i]); |
56 | if (!res) { |
57 | if (verbose) |
58 | fprintf(stderr, "%s %s", i ? "client" : "server", "socket closed\n"); |
59 | return res; |
60 | } |
61 | } |
62 | } |
63 | } |
64 | } |
65 | |
66 | /* Child process that finds out what to connect to and proxies |
67 | */ |
68 | void start_shoveler(int in_socket) |
69 | { |
70 | fd_set fds; |
71 | struct timeval tv; |
72 | int res = PROBE_AGAIN; |
73 | int out_socket; |
74 | struct connection cnx; |
75 | |
76 | init_cnx(&cnx); |
77 | cnx.q[0].fd = in_socket; |
78 | |
79 | FD_ZERO(&fds); |
80 | FD_SET(in_socket, &fds); |
81 | memset(&tv, 0, sizeof(tv)); |
82 | tv.tv_sec = probing_timeout; |
83 | |
84 | while (res == PROBE_AGAIN) { |
85 | /* POSIX does not guarantee that tv will be updated, but the client can |
86 | * only postpone the inevitable for so long */ |
87 | res = select(in_socket + 1, &fds, NULL, NULL, &tv); |
88 | if (res == -1) |
89 | perror("select"); |
90 | |
91 | if (FD_ISSET(in_socket, &fds)) { |
92 | /* Received data: figure out what protocol it is */ |
93 | res = probe_client_protocol(&cnx); |
94 | } else { |
95 | /* Timed out: it's necessarily SSH */ |
96 | cnx.proto = timeout_protocol(); |
97 | break; |
98 | } |
99 | } |
100 | |
101 | if (cnx.proto->service && |
102 | check_access_rights(in_socket, cnx.proto->service)) { |
103 | exit(0); |
104 | } |
105 | |
106 | /* Connect the target socket */ |
107 | out_socket = connect_addr(&cnx, in_socket); |
108 | CHECK_RES_DIE(out_socket, "connect"); |
109 | |
110 | cnx.q[1].fd = out_socket; |
111 | |
112 | log_connection(&cnx); |
113 | |
114 | flush_deferred(&cnx.q[1]); |
115 | |
116 | shovel(&cnx); |
117 | |
118 | close(in_socket); |
119 | close(out_socket); |
120 | |
121 | if (verbose) |
122 | fprintf(stderr, "connection closed down\n"); |
123 | |
124 | exit(0); |
125 | } |
126 | |
127 | static int *listener_pid; |
128 | static int listener_pid_number = 0; |
129 | |
130 | void stop_listeners(int sig) |
131 | { |
132 | int i; |
133 | |
134 | for (i = 0; i < listener_pid_number; i++) { |
135 | kill(listener_pid[i], sig); |
136 | } |
137 | } |
138 | |
139 | void main_loop(int listen_sockets[], int num_addr_listen) |
140 | { |
141 | int in_socket, i, res; |
142 | struct sigaction action; |
143 | |
144 | listener_pid_number = num_addr_listen; |
145 | listener_pid = malloc(listener_pid_number * sizeof(listener_pid[0])); |
146 | |
147 | /* Start one process for each listening address */ |
148 | for (i = 0; i < num_addr_listen; i++) { |
149 | if (!(listener_pid[i] = fork())) { |
150 | |
151 | /* Listening process just accepts a connection, forks, and goes |
152 | * back to listening */ |
153 | while (1) |
154 | { |
155 | in_socket = accept(listen_sockets[i], 0, 0); |
156 | if (verbose) fprintf(stderr, "accepted fd %d\n", in_socket); |
157 | |
158 | if (!fork()) |
159 | { |
160 | for (i = 0; i < num_addr_listen; ++i) |
161 | close(listen_sockets[i]); |
162 | start_shoveler(in_socket); |
163 | exit(0); |
164 | } |
165 | close(in_socket); |
166 | } |
167 | } |
168 | } |
169 | |
170 | /* Set SIGTERM to "stop_listeners" which further kills all listener |
171 | * processes. Note this won't kill processes that listeners forked, which |
172 | * means active connections remain active. */ |
173 | memset(&action, 0, sizeof(action)); |
174 | action.sa_handler = stop_listeners; |
175 | res = sigaction(SIGTERM, &action, NULL); |
176 | CHECK_RES_DIE(res, "sigaction"); |
177 | |
178 | wait(NULL); |
179 | } |
180 | |
181 | /* The actual main is in common.c: it's the same for both version of |
182 | * the server |
183 | */ |
184 | |
185 |
Built with git-ssb-web