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  * An http client that let's users 'ssh' to the
  30  * outside of the firewall by opening up a connection
  31  * through the http proxy.
  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_HTTPPROXYPORT   "80"
  57 #define CONNECT_STRLEN          256
  58 
  59 static int debug_flag = 0;
  60 
  61 static void
  62 usage(void)
  63 {
  64         (void) fprintf(stderr, gettext("Usage: ssh-http-proxy-connect "
  65             "[-h http_proxy_host] [-p http_proxy_port]\n"
  66             "remote_host remote_port\n"));
  67         exit(1);
  68 }
  69 
  70 /* PRINTFLIKE1 */
  71 static void
  72 debug(const char *format, ...)
  73 {
  74         char fmtbuf[BUFFER_SIZ];
  75         va_list args;
  76 
  77         if (debug_flag == 0) {
  78             return;
  79         }
  80         va_start(args, format);
  81         (void) snprintf(fmtbuf, sizeof (fmtbuf),
  82                 "ssh-http-proxy: %s\n", format);
  83         (void) vfprintf(stderr, fmtbuf, args);
  84         va_end(args);
  85 }
  86 
  87 static void
  88 signal_handler(int sig)
  89 {
  90         exit(0);
  91 }
  92 
  93 int
  94 main(int argc, char **argv)
  95 {
  96         extern char     *optarg;
  97         extern int      optind;
  98         int             retval, err_code, sock, ssh_port;
  99         int             version, ret_code;
 100         char            *httpproxy = NULL;
 101         char            *temp, *httpproxyport = NULL;
 102         char            *ssh_host;
 103         char            connect_str[CONNECT_STRLEN], connect_reply[BUFFER_SIZ];
 104         char            *ret_string;
 105         struct          addrinfo hints, *ai;
 106         struct          pollfd fds[2];
 107 
 108         /* Initialization for variables, set locale and textdomain */
 109 
 110         (void) setlocale(LC_ALL, "");
 111 
 112 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 113 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
 114 #endif
 115         (void) textdomain(TEXT_DOMAIN);
 116 
 117         /* Set up the signal handler */
 118         (void) signal(SIGINT, signal_handler);
 119         (void) signal(SIGPIPE, signal_handler);
 120         (void) signal(SIGPOLL, signal_handler);
 121 
 122         while ((retval = getopt(argc, argv, "dp:h:")) != -1) {
 123             switch (retval) {
 124                 case 'h':
 125                     httpproxy = optarg;
 126                     break;
 127                 case 'p':
 128                     httpproxyport = optarg;
 129                     break;
 130                 case 'd':
 131                     debug_flag = 1;
 132                     break;
 133                 default:
 134                     break;
 135             }
 136         }
 137 
 138         if (optind != argc - 2) {
 139                 usage();
 140         }
 141 
 142         ssh_host = argv[optind++];
 143         ssh_port = atoi(argv[optind]);
 144 
 145         /*
 146          * If the name of the http proxy were not
 147          * passed on the command line, try the
 148          * user's environment. First try HTTPPROXY.
 149          * If it's not set, try http_proxy.
 150          * Check the url specified for http_proxy
 151          * for errors.
 152          */
 153         if (httpproxy == NULL) {
 154             if ((httpproxy = getenv("HTTPPROXY")) == NULL) {
 155                 /* Try the other environment variable http_proxy */
 156                 if ((temp = getenv("http_proxy")) != NULL) {
 157                     temp += strlen("http://");
 158                     if (strpbrk(temp, ":") == NULL) {
 159                         /* Malformed url */
 160                         (void) fprintf(stderr, gettext("ssh-http-proxy: "
 161                             "Incorrect url specified for http_proxy "
 162                             "environment variable\n"));
 163                         exit(1);
 164                     }
 165                     httpproxy = strtok(temp, ":");
 166                     httpproxyport = strtok(NULL, "/");
 167                 } else {
 168                     (void) fprintf(stderr,
 169                         gettext("ssh-http-proxy: http proxy not specified\n"));
 170                     exit(1);
 171                 }
 172             }
 173         }
 174 
 175         /*
 176          * Extract the proxy port number from the user's environment.
 177          * Ignored if HTTPPROXY is not set.
 178          */
 179         if ((httpproxy != NULL) && (httpproxyport == NULL)) {
 180             if ((httpproxyport = getenv("HTTPPROXYPORT")) == NULL) {
 181                     httpproxyport = DEFAULT_HTTPPROXYPORT;
 182             }
 183         }
 184 
 185         debug("HTTPPROXY = %s", httpproxy);
 186         debug("HTTPPROXYPORT = %s", httpproxyport);
 187 
 188         bzero(&hints, sizeof (struct addrinfo));
 189         hints.ai_family = PF_UNSPEC;
 190         hints.ai_socktype = SOCK_STREAM;
 191 
 192         if ((err_code = getaddrinfo(httpproxy, httpproxyport, &hints, &ai))
 193             != 0) {
 194             (void) fprintf(stderr, "ssh-http-proxy: Unable to "
 195                 "perform name lookup\n");
 196             (void) fprintf(stderr, "%s: %s\n", httpproxy,
 197                 gai_strerror(err_code));
 198             exit(1);
 199         }
 200 
 201         if ((sock = socket(ai->ai_family, SOCK_STREAM, 0)) < 0) {
 202             perror("socket");
 203             exit(1);
 204         }
 205 
 206         /* Connect to the http proxy */
 207         if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
 208             (void) fprintf(stderr, gettext("ssh-http-proxy: Unable to connect"
 209                 " to %s: %s\n"), httpproxy, strerror(errno));
 210             (void) close(sock);
 211             exit(1);
 212         } else {
 213             /* Successful connection. */
 214             (void) snprintf(connect_str, sizeof (connect_str),
 215                 "CONNECT %s:%d HTTP/1.1\r\n\r\n", ssh_host, ssh_port);
 216             if (write(sock, &connect_str, strlen(connect_str)) < 0) {
 217                 perror("write");
 218                 (void) close(sock);
 219                 exit(1);
 220             }
 221 
 222             if (read(sock, connect_reply, sizeof (connect_reply)) == -1) {
 223                 perror("read");
 224                 (void) close(sock);
 225                 exit(1);
 226             }
 227 
 228             if (sscanf(connect_reply, "HTTP/1.%d %d",
 229                 &version, &ret_code) != 2) {
 230                 (void) fprintf(stderr,
 231                     gettext("ssh-http-proxy: HTTP reply not understood\n"));
 232                 (void) close(sock);
 233                 exit(1);
 234             }
 235 
 236             ret_string = strtok(connect_reply, "\n");
 237 
 238             /* If the return error code is not 200, print an error and quit. */
 239             if (ret_code != 200) {
 240                 (void) fprintf(stderr, "%s\n", ret_string);
 241                 (void) close(sock);
 242                 exit(1);
 243             } else {
 244                 debug("%s", ret_string);
 245             }
 246         }
 247 
 248         fds[0].fd = STDIN_FILENO;       /* Poll stdin for data. */
 249         fds[1].fd = sock;               /* Poll the socket for data. */
 250         fds[0].events = fds[1].events = POLLIN;
 251 
 252         for (;;) {
 253             if (poll(fds, 2, INFTIM) == -1) {
 254                 perror("poll");
 255                 (void) close(sock);
 256                 exit(1);
 257             }
 258 
 259             /* Data arrived on stdin, write it to the socket */
 260             if (fds[0].revents & POLLIN) {
 261                 if (proxy_read_write_loop(STDIN_FILENO, sock) == 0) {
 262                         (void) close(sock);
 263                         exit(1);
 264                 }
 265             } else if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
 266                 (void) close(sock);
 267                 exit(1);
 268             }
 269 
 270             /* Data arrived on the socket, write it to stdout */
 271             if (fds[1].revents & POLLIN) {
 272                 if (proxy_read_write_loop(sock, STDOUT_FILENO) == 0) {
 273                         (void) close(sock);
 274                         exit(1);
 275                 }
 276             } else if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) {
 277                 (void) close(sock);
 278                 exit(1);
 279             }
 280         }
 281 
 282         /* NOTREACHED */
 283         return (0);
 284 }