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