123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- /*
- * A WebSocket to TCP socket proxy with support for "wss://" encryption.
- * Copyright 2010 Joel Martin
- * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
- *
- * You can make a cert/key with openssl using:
- * openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
- * as taken from http://docs.python.org/dev/library/ssl.html#certificates
- */
- #include <stdio.h>
- #include <errno.h>
- #include <limits.h>
- #include <getopt.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <sys/select.h>
- #include <fcntl.h>
- #include <sys/stat.h>
- #include "websocket.h"
- char traffic_legend[] = "\n\
- Traffic Legend:\n\
- } - Client receive\n\
- }. - Client receive partial\n\
- { - Target receive\n\
- \n\
- > - Target send\n\
- >. - Target send partial\n\
- < - Client send\n\
- <. - Client send partial\n\
- ";
- char USAGE[] = "Usage: [options] " \
- "[source_addr:]source_port target_addr:target_port\n\n" \
- " --cert CERT load CERT as SSL certificate\n" \
- " --foreground|-f run in the foreground\n" \
- " --ssl-only disallow non-SSL connections";
- #define usage(fmt, args...) \
- fprintf(stderr, "%s\n\n", USAGE); \
- fprintf(stderr, fmt , ## args); \
- exit(1);
- char target_host[256];
- int target_port;
- extern pipe_error;
- extern settings_t settings;
- extern char *tbuf, *cbuf, *tbuf_tmp, *cbuf_tmp;
- extern unsigned int bufsize, dbufsize;
- void do_proxy(ws_ctx_t *ws_ctx, int target) {
- fd_set rlist, wlist, elist;
- struct timeval tv;
- int i, maxfd, client = ws_ctx->sockfd;
- unsigned int tstart, tend, cstart, cend, ret;
- ssize_t len, bytes;
- tstart = tend = cstart = cend = 0;
- maxfd = client > target ? client+1 : target+1;
- while (1) {
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- FD_ZERO(&rlist);
- FD_ZERO(&wlist);
- FD_ZERO(&elist);
- FD_SET(client, &elist);
- FD_SET(target, &elist);
- if (tend == tstart) {
- // Nothing queued for target, so read from client
- FD_SET(client, &rlist);
- } else {
- // Data queued for target, so write to it
- FD_SET(target, &wlist);
- }
- if (cend == cstart) {
- // Nothing queued for client, so read from target
- FD_SET(target, &rlist);
- } else {
- // Data queued for client, so write to it
- FD_SET(client, &wlist);
- }
- ret = select(maxfd, &rlist, &wlist, &elist, &tv);
- if (pipe_error) { break; }
- if (FD_ISSET(target, &elist)) {
- handler_emsg("target exception\n");
- break;
- }
- if (FD_ISSET(client, &elist)) {
- handler_emsg("client exception\n");
- break;
- }
- if (ret == -1) {
- handler_emsg("select(): %s\n", strerror(errno));
- break;
- } else if (ret == 0) {
- //handler_emsg("select timeout\n");
- continue;
- }
- if (FD_ISSET(target, &wlist)) {
- len = tend-tstart;
- bytes = send(target, tbuf + tstart, len, 0);
- if (pipe_error) { break; }
- if (bytes < 0) {
- handler_emsg("target connection error: %s\n",
- strerror(errno));
- break;
- }
- tstart += bytes;
- if (tstart >= tend) {
- tstart = tend = 0;
- traffic(">");
- } else {
- traffic(">.");
- }
- }
- if (FD_ISSET(client, &wlist)) {
- len = cend-cstart;
- bytes = ws_send(ws_ctx, cbuf + cstart, len);
- if (pipe_error) { break; }
- if (len < 3) {
- handler_emsg("len: %d, bytes: %d: %d\n", len, bytes, *(cbuf + cstart));
- }
- cstart += bytes;
- if (cstart >= cend) {
- cstart = cend = 0;
- traffic("<");
- } else {
- traffic("<.");
- }
- }
- if (FD_ISSET(target, &rlist)) {
- bytes = recv(target, cbuf_tmp, dbufsize , 0);
- if (pipe_error) { break; }
- if (bytes <= 0) {
- handler_emsg("target closed connection\n");
- break;
- }
- cstart = 0;
- cend = encode(cbuf_tmp, bytes, cbuf, bufsize);
- /*
- printf("encoded: ");
- for (i=0; i< cend; i++) {
- printf("%u,", (unsigned char) *(cbuf+i));
- }
- printf("\n");
- */
- if (cend < 0) {
- handler_emsg("encoding error\n");
- break;
- }
- traffic("{");
- }
- if (FD_ISSET(client, &rlist)) {
- bytes = ws_recv(ws_ctx, tbuf_tmp, bufsize-1);
- if (pipe_error) { break; }
- if (bytes <= 0) {
- handler_emsg("client closed connection\n");
- break;
- } else if ((bytes == 2) &&
- (tbuf_tmp[0] == '\xff') &&
- (tbuf_tmp[1] == '\x00')) {
- handler_emsg("client sent orderly close frame\n");
- break;
- }
- /*
- printf("before decode: ");
- for (i=0; i< bytes; i++) {
- printf("%u,", (unsigned char) *(tbuf_tmp+i));
- }
- printf("\n");
- */
- len = decode(tbuf_tmp, bytes, tbuf, bufsize-1);
- /*
- printf("decoded: ");
- for (i=0; i< len; i++) {
- printf("%u,", (unsigned char) *(tbuf+i));
- }
- printf("\n");
- */
- if (len < 0) {
- handler_emsg("decoding error\n");
- break;
- }
- traffic("}");
- tstart = 0;
- tend = len;
- }
- }
- }
- void proxy_handler(ws_ctx_t *ws_ctx) {
- int tsock = 0;
- struct sockaddr_in taddr;
- handler_msg("connecting to: %s:%d\n", target_host, target_port);
- tsock = socket(AF_INET, SOCK_STREAM, 0);
- if (tsock < 0) {
- handler_emsg("Could not create target socket: %s\n",
- strerror(errno));
- return;
- }
- bzero((char *) &taddr, sizeof(taddr));
- taddr.sin_family = AF_INET;
- taddr.sin_port = htons(target_port);
- /* Resolve target address */
- if (resolve_host(&taddr.sin_addr, target_host) < -1) {
- handler_emsg("Could not resolve target address: %s\n",
- strerror(errno));
- }
- if (connect(tsock, (struct sockaddr *) &taddr, sizeof(taddr)) < 0) {
- handler_emsg("Could not connect to target: %s\n",
- strerror(errno));
- close(tsock);
- return;
- }
- if ((settings.verbose) && (! settings.daemon)) {
- printf("%s", traffic_legend);
- }
- do_proxy(ws_ctx, tsock);
- close(tsock);
- }
- int main(int argc, char *argv[])
- {
- int fd, c, option_index = 0;
- static int ssl_only = 0, foreground = 0, verbose = 0;
- char *found;
- static struct option long_options[] = {
- {"verbose", no_argument, &verbose, 'v'},
- {"ssl-only", no_argument, &ssl_only, 1 },
- {"foreground", no_argument, &foreground, 'f'},
- /* ---- */
- {"cert", required_argument, 0, 'c'},
- {0, 0, 0, 0}
- };
- settings.cert = realpath("self.pem", NULL);
- while (1) {
- c = getopt_long (argc, argv, "vfc:",
- long_options, &option_index);
- /* Detect the end */
- if (c == -1) { break; }
- switch (c) {
- case 0:
- break; // ignore
- case 1:
- break; // ignore
- case 'v':
- verbose = 1;
- break;
- case 'f':
- foreground = 1;
- break;
- case 'r':
- if ((fd = open(optarg, O_CREAT,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < -1) {
- usage("Could not access %s\n", optarg);
- }
- close(fd);
- break;
- case 'c':
- settings.cert = realpath(optarg, NULL);
- if (! settings.cert) {
- usage("No cert file at %s\n", optarg);
- }
- break;
- default:
- usage("");
- }
- }
- settings.verbose = verbose;
- settings.ssl_only = ssl_only;
- settings.daemon = foreground ? 0: 1;
- if ((argc-optind) != 2) {
- usage("Invalid number of arguments\n");
- }
- found = strstr(argv[optind], ":");
- if (found) {
- memcpy(settings.listen_host, argv[optind], found-argv[optind]);
- settings.listen_port = strtol(found+1, NULL, 10);
- } else {
- settings.listen_host[0] = '\0';
- settings.listen_port = strtol(argv[optind], NULL, 10);
- }
- optind++;
- if (settings.listen_port == 0) {
- usage("Could not parse listen_port\n");
- }
- found = strstr(argv[optind], ":");
- if (found) {
- memcpy(target_host, argv[optind], found-argv[optind]);
- target_port = strtol(found+1, NULL, 10);
- } else {
- usage("Target argument must be host:port\n");
- }
- if (target_port == 0) {
- usage("Could not parse target port\n");
- }
- if (ssl_only) {
- printf("cert: %s\n", settings.cert);
- if (!settings.cert || !access(settings.cert)) {
- usage("SSL only and cert file not found\n");
- }
- }
- //printf(" verbose: %d\n", settings.verbose);
- //printf(" ssl_only: %d\n", settings.ssl_only);
- //printf(" daemon: %d\n", settings.daemon);
- //printf(" cert: %s\n", settings.cert);
- settings.handler = proxy_handler;
- start_server();
- free(tbuf);
- free(cbuf);
- free(tbuf_tmp);
- free(cbuf_tmp);
- }
|