1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 * 22 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * A SOCKS client that let's users 'ssh' to the 30 * outside of the firewall by opening up a connection 31 * through the SOCKS server. Supports only SOCKS v5. 32 */ 33 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <netdb.h> 38 #include <strings.h> 39 #include <unistd.h> 40 #include <inttypes.h> 41 #include <errno.h> 42 #include <poll.h> 43 #include <signal.h> 44 #include <locale.h> 45 #include <libintl.h> 46 #include <netinet/in.h> 47 #include <sys/types.h> 48 #include <sys/socket.h> 49 #include <arpa/inet.h> 50 #include <sys/time.h> 51 #include <sys/stropts.h> 52 #include <sys/stat.h> 53 #include <sys/varargs.h> 54 #include "proxy-io.h" 55 56 #define DEFAULT_SOCKS5_PORT "1080" 57 58 static int debug_flag = 0; 59 60 static void 61 usage(void) 62 { 63 (void) fprintf(stderr, gettext("Usage: ssh-socks5-proxy-connect " 64 "[-h socks5_proxy_host] [-p socks5_proxy_port] \n" 65 "remote_host remote_port\n")); 66 exit(1); 67 } 68 69 /* PRINTFLIKE1 */ 70 static void 71 debug(const char *format, ...) 72 { 73 char fmtbuf[BUFFER_SIZ]; 74 va_list args; 75 76 if (debug_flag == 0) { 77 return; 78 } 79 va_start(args, format); 80 (void) snprintf(fmtbuf, sizeof (fmtbuf), 81 "ssh-socks5-proxy: %s\n", format); 82 (void) vfprintf(stderr, fmtbuf, args); 83 va_end(args); 84 } 85 86 static void 87 signal_handler(int sig) 88 { 89 exit(0); 90 } 91 92 static int 93 do_version_exchange(int sockfd) 94 { 95 char buffer[3], recv_buf[2]; 96 97 buffer[0] = 0x05; /* VER */ 98 buffer[1] = 0x01; /* NMETHODS */ 99 buffer[2] = 0x00; /* METHODS */ 100 101 if (write(sockfd, &buffer, sizeof (buffer)) < 0) { 102 perror("write"); 103 return (0); 104 } 105 106 if (read(sockfd, &recv_buf, sizeof (recv_buf)) == -1) { 107 perror("read"); 108 return (0); 109 } 110 111 /* 112 * No need to check the server's version as per 113 * the protocol spec. Check the method supported 114 * by the server. Currently if the server does not 115 * support NO AUTH, we disconnect. 116 */ 117 if (recv_buf[1] != 0x00) { 118 debug("Unsupported Authentication Method"); 119 return (0); 120 } 121 122 /* Return success. */ 123 return (1); 124 } 125 126 static void 127 send_request( 128 int sockfd, 129 const char *ssh_host, 130 uchar_t ssh_host_len, 131 uint16_t *ssh_port) 132 { 133 int failure = 1; 134 char *buffer, *temp, recv_buf[BUFFER_SIZ]; 135 uchar_t version = 0x05, cmd = 0x01, rsv = 0x00, atyp = 0x03; 136 137 buffer = malloc(strlen(ssh_host) + 7); 138 139 temp = buffer; 140 141 /* Assemble the request packet */ 142 (void) memcpy(temp, &version, sizeof (version)); 143 temp += sizeof (version); 144 (void) memcpy(temp, &cmd, sizeof (cmd)); 145 temp += sizeof (cmd); 146 (void) memcpy(temp, &rsv, sizeof (rsv)); 147 temp += sizeof (rsv); 148 (void) memcpy(temp, &atyp, sizeof (atyp)); 149 temp += sizeof (atyp); 150 (void) memcpy(temp, &ssh_host_len, sizeof (ssh_host_len)); 151 temp += sizeof (ssh_host_len); 152 (void) memcpy(temp, ssh_host, strlen(ssh_host)); 153 temp += strlen(ssh_host); 154 (void) memcpy(temp, ssh_port, sizeof (*ssh_port)); 155 temp += sizeof (*ssh_port); 156 157 if (write(sockfd, buffer, temp - buffer) == -1) { 158 perror("write"); 159 exit(1); 160 } 161 162 if (read(sockfd, &recv_buf, sizeof (recv_buf)) == -1) { 163 perror("read"); 164 exit(1); 165 } 166 167 /* temp now points to the recieve buffer. */ 168 temp = recv_buf; 169 170 /* Check the server's version. */ 171 if (*temp++ != 0x05) { 172 (void) fprintf(stderr, gettext("Unsupported SOCKS version: %x\n"), 173 recv_buf[0]); 174 exit(1); 175 } 176 177 /* Check server's reply */ 178 switch (*temp++) { 179 case 0x00: 180 failure = 0; 181 debug("CONNECT command Succeeded."); 182 break; 183 case 0x01: 184 debug("General SOCKS server failure."); 185 break; 186 case 0x02: 187 debug("Connection not allowed by ruleset."); 188 break; 189 case 0x03: 190 debug("Network Unreachable."); 191 break; 192 case 0x04: 193 debug("Host unreachable."); 194 break; 195 case 0x05: 196 debug("Connection refused."); 197 break; 198 case 0x06: 199 debug("TTL expired."); 200 break; 201 case 0x07: 202 debug("Command not supported"); 203 break; 204 case 0x08: 205 debug("Address type not supported."); 206 break; 207 default: 208 (void) fprintf(stderr, gettext("ssh-socks5-proxy: " 209 "SOCKS Server reply not understood\n")); 210 } 211 212 if (failure == 1) { 213 exit(1); 214 } 215 216 /* Parse the rest of the packet */ 217 218 /* Ignore RSV */ 219 temp++; 220 221 /* Check ATYP */ 222 if (*temp != 0x01) { 223 (void) fprintf(stderr, gettext("ssh-socks5-proxy: " 224 "Address type not supported: %u\n"), *temp); 225 exit(1); 226 } 227 228 free(buffer); 229 } 230 231 int 232 main(int argc, char **argv) 233 { 234 extern char *optarg; 235 extern int optind; 236 int retval, err_code, sock; 237 uint16_t ssh_port; 238 uchar_t ssh_host_len; 239 char *socks_server = NULL, *socks_port = NULL; 240 char *ssh_host; 241 struct addrinfo hints, *ai; 242 struct pollfd fds[2]; 243 244 /* Initialization for variables, set locale and textdomain */ 245 246 (void) setlocale(LC_ALL, ""); 247 248 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 249 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 250 #endif 251 (void) textdomain(TEXT_DOMAIN); 252 253 /* Set up the signal handler */ 254 (void) signal(SIGINT, signal_handler); 255 (void) signal(SIGPIPE, signal_handler); 256 (void) signal(SIGPOLL, signal_handler); 257 258 while ((retval = getopt(argc, argv, "dp:h:")) != -1) { 259 switch (retval) { 260 case 'h': 261 socks_server = optarg; 262 break; 263 case 'p': 264 socks_port = optarg; 265 break; 266 case 'd': 267 debug_flag = 1; 268 break; 269 default: 270 break; 271 } 272 } 273 274 if (optind != argc - 2) { 275 usage(); 276 } 277 278 ssh_host = argv[optind++]; 279 ssh_host_len = (uchar_t)strlen(ssh_host); 280 ssh_port = htons(atoi(argv[optind])); 281 282 /* 283 * If the name and/or port number of the 284 * socks server were not passed on the 285 * command line, try the user's environment. 286 */ 287 if (socks_server == NULL) { 288 if ((socks_server = getenv("SOCKS5_SERVER")) == NULL) { 289 (void) fprintf(stderr, gettext("ssh-socks5-proxy: " 290 "SOCKS5 SERVER not specified\n")); 291 exit(1); 292 } 293 } 294 if (socks_port == NULL) { 295 if ((socks_port = getenv("SOCKS5_PORT")) == NULL) { 296 socks_port = DEFAULT_SOCKS5_PORT; 297 } 298 } 299 300 debug("SOCKS5_SERVER = %s", socks_server); 301 debug("SOCKS5_PORT = %s", socks_port); 302 303 bzero(&hints, sizeof (struct addrinfo)); 304 hints.ai_family = PF_UNSPEC; 305 hints.ai_socktype = SOCK_STREAM; 306 307 if ((err_code = getaddrinfo(socks_server, socks_port, &hints, &ai)) 308 != 0) { 309 (void) fprintf(stderr, "%s: %s\n", socks_server, 310 gai_strerror(err_code)); 311 exit(1); 312 } 313 314 if ((sock = socket(ai->ai_family, SOCK_STREAM, 0)) < 0) { 315 perror("socket"); 316 exit(1); 317 } 318 319 /* Connect to the SOCKS server */ 320 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == 0) { 321 debug("Connected to the SOCKS server"); 322 /* Do the SOCKS v5 communication with the server. */ 323 if (do_version_exchange(sock) > 0) { 324 debug("Done version exchange"); 325 send_request(sock, ssh_host, ssh_host_len, &ssh_port); 326 } else { 327 (void) fprintf(stderr, gettext("ssh-socks5-proxy: Client and " 328 "Server versions differ.\n")); 329 (void) close(sock); 330 exit(1); 331 } 332 } else { 333 perror("connect"); 334 (void) close(sock); 335 exit(1); 336 } 337 338 fds[0].fd = STDIN_FILENO; /* Poll stdin for data. */ 339 fds[1].fd = sock; /* Poll the socket for data. */ 340 fds[0].events = fds[1].events = POLLIN; 341 342 for (;;) { 343 if (poll(fds, 2, INFTIM) == -1) { 344 perror("poll"); 345 (void) close(sock); 346 exit(1); 347 } 348 349 /* Data arrived on stdin, write it to the socket */ 350 if (fds[0].revents & POLLIN) { 351 if (proxy_read_write_loop(STDIN_FILENO, sock) == 0) { 352 (void) close(sock); 353 exit(1); 354 } 355 } else if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { 356 (void) close(sock); 357 exit(1); 358 } 359 360 /* Data arrived on the socket, write it to stdout */ 361 if (fds[1].revents & POLLIN) { 362 if (proxy_read_write_loop(sock, STDOUT_FILENO) == 0) { 363 (void) close(sock); 364 exit(1); 365 } 366 } else if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) { 367 (void) close(sock); 368 exit(1); 369 } 370 } 371 372 /* NOTREACHED */ 373 return (0); 374 } 375