Files: 2781c75ff99cd5da7b25f6b883d78ff3de987308 / sslh-fork.c
4513 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 | struct addrinfo *saddr; |
73 | int res; |
74 | int out_socket; |
75 | struct connection cnx; |
76 | struct proto *prot; |
77 | |
78 | init_cnx(&cnx); |
79 | |
80 | FD_ZERO(&fds); |
81 | FD_SET(in_socket, &fds); |
82 | memset(&tv, 0, sizeof(tv)); |
83 | tv.tv_sec = probing_timeout; |
84 | res = select(in_socket + 1, &fds, NULL, NULL, &tv); |
85 | if (res == -1) |
86 | perror("select"); |
87 | |
88 | cnx.q[0].fd = in_socket; |
89 | |
90 | if (FD_ISSET(in_socket, &fds)) { |
91 | /* Received data: figure out what protocol it is */ |
92 | prot = probe_client_protocol(&cnx); |
93 | } else { |
94 | /* Timed out: it's necessarily SSH */ |
95 | prot = timeout_protocol(); |
96 | } |
97 | |
98 | saddr = prot->saddr; |
99 | if (prot->service && |
100 | check_access_rights(in_socket, prot->service)) { |
101 | exit(0); |
102 | } |
103 | |
104 | /* Connect the target socket */ |
105 | out_socket = connect_addr(saddr, in_socket, prot->description); |
106 | CHECK_RES_DIE(out_socket, "connect"); |
107 | |
108 | cnx.q[1].fd = out_socket; |
109 | |
110 | log_connection(&cnx); |
111 | |
112 | flush_defered(&cnx.q[1]); |
113 | |
114 | shovel(&cnx); |
115 | |
116 | close(in_socket); |
117 | close(out_socket); |
118 | |
119 | if (verbose) |
120 | fprintf(stderr, "connection closed down\n"); |
121 | |
122 | exit(0); |
123 | } |
124 | |
125 | static int *listener_pid; |
126 | static int listener_pid_number = 0; |
127 | |
128 | void stop_listeners(int sig) |
129 | { |
130 | int i; |
131 | |
132 | for (i = 0; i < listener_pid_number; i++) { |
133 | kill(listener_pid[i], sig); |
134 | } |
135 | } |
136 | |
137 | void main_loop(int listen_sockets[], int num_addr_listen) |
138 | { |
139 | int in_socket, i, res; |
140 | struct sigaction action; |
141 | |
142 | listener_pid_number = num_addr_listen; |
143 | listener_pid = malloc(listener_pid_number * sizeof(listener_pid[0])); |
144 | |
145 | /* Start one process for each listening address */ |
146 | for (i = 0; i < num_addr_listen; i++) { |
147 | if (!(listener_pid[i] = fork())) { |
148 | |
149 | /* Listening process just accepts a connection, forks, and goes |
150 | * back to listening */ |
151 | while (1) |
152 | { |
153 | in_socket = accept(listen_sockets[i], 0, 0); |
154 | if (verbose) fprintf(stderr, "accepted fd %d\n", in_socket); |
155 | |
156 | if (!fork()) |
157 | { |
158 | close(listen_sockets[i]); |
159 | start_shoveler(in_socket); |
160 | exit(0); |
161 | } |
162 | close(in_socket); |
163 | } |
164 | } |
165 | } |
166 | |
167 | /* Set SIGTERM to "stop_listeners" which further kills all listener |
168 | * processes. Note this won't kill processes that listeners forked, which |
169 | * means active connections remain active. */ |
170 | memset(&action, 0, sizeof(action)); |
171 | action.sa_handler = stop_listeners; |
172 | res = sigaction(SIGTERM, &action, NULL); |
173 | CHECK_RES_DIE(res, "sigaction"); |
174 | |
175 | wait(NULL); |
176 | } |
177 | |
178 | /* The actual main is in common.c: it's the same for both version of |
179 | * the server |
180 | */ |
181 | |
182 |
Built with git-ssb-web