git ssb

0+

cel / sslh



Tree: d7e9cbb655626718177c36988d3b63a579dbfb66

Files: d7e9cbb655626718177c36988d3b63a579dbfb66 / sslh.c

8807 bytesRaw
1/*
2 Reimplementation of sslh in C
3
4# Copyright (C) 2007 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
21Comments? questions? sslh@rutschle.net
22
23Compilation instructions:
24
25Solaris:
26 cc -o sslh sslh.c -lresolv -lsocket -lnsl
27
28LynxOS:
29 gcc -o tcproxy tcproxy.c -lnetinet
30
31*/
32
33#define VERSION "1.0"
34
35#include <sys/types.h>
36#include <fcntl.h>
37#include <string.h>
38#include <unistd.h>
39#include <stdlib.h>
40#include <stdio.h>
41#include <signal.h>
42#include <sys/socket.h>
43#include <netinet/in.h>
44#include <arpa/inet.h>
45#include <netdb.h>
46#include <pwd.h>
47
48#define CHECK_RES_DIE(res, str) \
49if (res == -1) { \
50 perror(str); \
51 exit(1); \
52}
53
54#define USAGE_STRING "usage:\n\tsslh [-t <timeout>] -u <username> -p <listenport> -s [sshhost:]port -l [sslhost:]port [-v]\n"
55
56
57/* Starts a listening socket on specified port.
58 Returns file descriptor
59 */
60int start_listen_socket(int port)
61{
62 struct sockaddr_in saddr;
63 int sockfd, res, reuse;
64
65 sockfd = socket(AF_INET, SOCK_STREAM, 0);
66 CHECK_RES_DIE(sockfd, "socket");
67
68 reuse = 1;
69 res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
70 CHECK_RES_DIE(res, "setsockopt");
71
72 memset(&saddr, 0, sizeof(saddr));
73 saddr.sin_port = htons(port);
74
75 res = bind (sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
76 CHECK_RES_DIE(res, "bind");
77
78 res = listen (sockfd, 5);
79 CHECK_RES_DIE(res, "listen");
80
81 return sockfd;
82}
83
84
85/*
86 * moves data from one fd to other
87 * returns 0 if incoming socket closed, size moved otherwise
88 */
89int fd2fd(int target, int from)
90{
91 char buffer[BUFSIZ];
92 int size;
93
94 size = read(from, buffer, sizeof(buffer));
95 CHECK_RES_DIE(size, "read");
96
97 if (size == 0)
98 return 0;
99
100 size = write(target, buffer, size);
101 CHECK_RES_DIE(size, "write");
102
103 return size;
104}
105
106/* shovels data from one fd to the other and vice-versa
107 returns after one socket closed
108 */
109int shovel(int fd1, int fd2)
110{
111 fd_set fds;
112 int res;
113
114 FD_ZERO(&fds);
115 while (1) {
116 FD_SET(fd1, &fds);
117 FD_SET(fd2, &fds);
118
119 res = select(
120 (fd1 > fd2 ? fd1 : fd2 ) + 1,
121 &fds,
122 NULL,
123 NULL,
124 NULL
125 );
126 CHECK_RES_DIE(res, "select");
127
128 if (FD_ISSET(fd1, &fds)) {
129 res = fd2fd(fd2, fd1);
130 if (!res) {
131 printf("client socket closed\n");
132 return res;
133 }
134 }
135
136 if (FD_ISSET(fd2, &fds)) {
137 res = fd2fd(fd1, fd2);
138 if (!res) {
139 printf("server socket closed\n");
140 return res;
141 }
142 }
143 }
144}
145
146/* returns a static string that prints the IP and port of the sockaddr */
147char* get_addr(struct sockaddr* s)
148{
149 char addr_str[1024];
150 static char addr_name[1024];
151
152 inet_ntop(AF_INET, &((struct sockaddr_in*)s)->sin_addr, addr_str, sizeof(addr_str));
153 snprintf(addr_name, sizeof(addr_name), "%s:%d", addr_str,ntohs(((struct sockaddr_in*)s)->sin_port));
154 return addr_name;
155}
156
157/* turns a "hostname:port" string into a struct sockaddr;
158sock: socket address to which to copy the addr
159fullname: input string -- it gets clobbered
160serv: default service/port
161(defaults don't work yet)
162*/
163int resolve_name(struct sockaddr *sock, char* fullname, int port) {
164 struct addrinfo *addr, hint;
165 char *serv, *host;
166 int res;
167
168 char *sep = strchr(fullname, ':');
169
170 if (!sep) /* No separator: parameter is just a port */
171 {
172 serv = fullname;
173 fprintf(stderr, "names must be fully specified as hostname:port for the moment\n");
174 exit(1);
175 }
176 else {
177 host = fullname;
178 serv = sep+1;
179 *sep = 0;
180 }
181
182 memset(&hint, 0, sizeof(hint));
183 hint.ai_family = PF_INET;
184 hint.ai_socktype = SOCK_STREAM;
185
186 res = getaddrinfo(host, serv, &hint, &addr);
187 if (res) {
188 fprintf(stderr, "%s\n", gai_strerror(res));
189 if (res == EAI_SERVICE)
190 fprintf(stderr, "(Check you have specified all ports)\n");
191 exit(1);
192 }
193
194 memcpy(sock, addr->ai_addr, sizeof(*sock));
195}
196
197/*
198 * Settings that depend on the command line.
199 * They're set in main(), but also used in start_shoveler(), and it'd be heavy-handed
200 * to pass it all as parameters
201 */
202int verbose = 0;
203int timeout = 2;
204int listen_port = 443;
205struct sockaddr addr_ssl, addr_ssh;
206
207
208/* Child process that finds out what to connect to and proxies
209 */
210void start_shoveler(int in_socket)
211{
212 fd_set fds;
213 struct timeval tv;
214 struct sockaddr *saddr;
215 int res;
216 int out_socket;
217
218 FD_ZERO(&fds);
219 FD_SET(in_socket, &fds);
220 memset(&tv, 0, sizeof(tv));
221 tv.tv_sec = timeout;
222 res = select(in_socket + 1, &fds, NULL, NULL, &tv);
223 if (res == -1)
224 perror("select");
225
226 /* Pick the target address depending on whether we timed out or not */
227 if (FD_ISSET(in_socket, &fds)) {
228 /* The client wrote something to the socket: it's an SSL connection */
229 saddr = &addr_ssl;
230 if (verbose)
231 fprintf(stderr, "Forwarding to SSL\n");
232 } else {
233 /* The client hasn't written anything and we timed out: connect to SSH */
234 saddr = &addr_ssh;
235 if (verbose)
236 fprintf(stderr, "Forwarding to SSH\n");
237 }
238
239 /* Connect the target socket */
240 out_socket = socket(AF_INET, SOCK_STREAM, 0);
241 res = connect(out_socket, saddr, sizeof(addr_ssl));
242 CHECK_RES_DIE(res, "connect");
243 if (verbose)
244 fprintf(stderr, "connected to something\n");
245
246 shovel(in_socket, out_socket);
247
248 close(in_socket);
249 close(out_socket);
250
251 if (verbose)
252 fprintf(stderr, "connection closed down\n");
253
254 exit(0);
255}
256
257/* SIGCHLD handling:
258 * we need to reap our children
259 */
260void child_handler(int signo)
261{
262 signal(SIGCHLD, &child_handler);
263 wait(NULL);
264}
265void setup_signals(void)
266{
267 int res;
268
269 res = (int)signal(SIGCHLD, &child_handler);
270 CHECK_RES_DIE(res, "signal");
271}
272
273
274/* We don't want to run as root -- drop priviledges if required */
275void drop_privileges(char* user_name)
276{
277 int res;
278 struct passwd *pw = getpwnam(user_name);
279 if (!pw) {
280 fprintf(stderr, "%s: not found\n", user_name);
281 exit(1);
282 }
283 if (verbose)
284 fprintf(stderr, "turning into %s\n", user_name);
285
286 res = setgid(pw->pw_gid);
287 CHECK_RES_DIE(res, "setgid");
288 setuid(pw->pw_uid);
289 CHECK_RES_DIE(res, "setuid");
290}
291
292int main(int argc, char *argv[])
293{
294
295 extern char *optarg;
296 extern int optind;
297 int c, res;
298
299 int in_socket, out_socket, listen_socket;
300
301 /* Init defaults */
302 char *user_name = "nobody";
303 char ssl_str[] = "localhost:443"; /* need to copy -- Linux doesn't let write to BSS? */
304 char ssh_str[] = "localhost:22";
305 resolve_name(&addr_ssl, ssl_str, 443);
306 resolve_name(&addr_ssh, ssh_str, 22);
307
308 while ((c = getopt(argc, argv, "t:l:s:p:vu:")) != EOF) {
309 switch (c) {
310
311 case 't':
312 timeout = atoi(optarg);
313 break;
314
315 case 'p':
316 listen_port = atoi(optarg);
317 break;
318
319 case 'l':
320 resolve_name(&addr_ssl, optarg, 443);
321 break;
322
323 case 's':
324 resolve_name(&addr_ssh, optarg, 22);
325 break;
326
327 case 'v':
328 verbose += 1;
329 break;
330
331 case 'u':
332 user_name = optarg;
333 break;
334
335 default:
336 fprintf(stderr, USAGE_STRING);
337 exit(2);
338 }
339 }
340
341 if (verbose) {
342 fprintf(stderr, "SSL addr: %s (after timeout %ds)\n", get_addr(&addr_ssl), timeout);
343 fprintf(stderr, "SSH addr: %s\n", get_addr(&addr_ssh));
344 fprintf(stderr, "listening on port %d\n", listen_port);
345 }
346
347
348 setup_signals();
349
350 listen_socket = start_listen_socket(listen_port);
351
352 drop_privileges(user_name);
353
354 /* Main server loop: accept connections, find what they are, fork shovelers */
355 while (1)
356 {
357 in_socket = accept(listen_socket, 0, 0);
358 fprintf(stderr, "accepted fd %d\n", in_socket);
359
360 if (!fork())
361 {
362 start_shoveler(in_socket);
363 exit(0);
364 }
365 close(in_socket);
366 }
367
368 return 0;
369}
370
371
372

Built with git-ssb-web