/* remember to setenv snews_proxy and https_proxy = "http://127.0.0.1:5010/" */
/* gcc -O6 -I/usr/local/ssl/include lxp.c -lssl -lcrypto -s -o lxp */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <resolv.h>
#include "ssl.h"
int main(int argc, char *argv[])
{
  SSL_CTX *ssl_ctx;
  SSL *ssl_conn;
  int n, flag, lstn, acpt = -1, rnet = -1;
  char *cp, *pp, dns[128], xbuf[4096];
  fd_set fds;
  struct sockaddr_in sin, rsin;
  struct hostent *host;

  memset((char *) &sin, 0, sizeof(sin));
  sin.sin_family = AF_INET, rsin = sin, sin.sin_port = htons(5010);
  lstn = socket(AF_INET, SOCK_STREAM, 0);
  if (0 > bind(lstn, (struct sockaddr *) &sin, sizeof(sin)))
    exit(-1);
  SSLeay_add_ssl_algorithms(), ssl_ctx = SSL_CTX_new(SSLv23_client_method());
  SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL);
  SSL_CTX_set_default_verify_paths(ssl_ctx);
  listen(lstn, 3);
  for (;;) {
    close(acpt), close(rnet);
    FD_ZERO(&fds);
    FD_SET(lstn, &fds);
    select(lstn + 1, &fds, NULL, NULL, NULL);
    n = sizeof(sin);
    acpt = accept(lstn, (struct sockaddr *) &sin, &n);
    flag = 0;
    do {                        /* get entire http header */
      if ((n = read(acpt, &xbuf[flag], 4096 - flag)) < 0)
        break;
      flag += n, xbuf[flag] = 0;
    } while (!strstr(xbuf, "\r\n\r\n"));
    if (n < 0)
      continue;
    cp = strchr(xbuf, ' ');     /* point at url */
    if (!strncmp(++cp, "https://", 8))
      rsin.sin_port = htons(443);
    else if (!strncmp(cp, "snews://", 8))
      rsin.sin_port = htons(563);
    else
      continue;
    cp += 8;
    if ((pp = strchr(cp, '/')) || (pp = strchr(cp, ' '))
        || (pp = strchr(cp, '\r')))
      *pp++ = 0;                /* isolate hostname */
    strncpy(dns, cp, 125);
    if ((cp = strchr(dns, ':')))
      *cp++ = 0, rsin.sin_port = htons(atoi(cp));
    n = inet_addr(dns);
    if (n != -1)
      memcpy(&rsin.sin_addr, &n, sizeof(n));
    else if ((host = gethostbyname(dns)) != NULL)
      memcpy(&rsin.sin_addr, host->h_addr, host->h_length);
    else
      continue;
    rnet = socket(AF_INET, SOCK_STREAM, 0);
    if ((n = connect(rnet, (struct sockaddr *) &(rsin), sizeof(sin))) < 0)
      continue;
    SSL_set_fd(ssl_conn = SSL_new(ssl_ctx), rnet);
    if ((n = SSL_connect(ssl_conn)) < 0)
      continue;
    if ((cp = strstr(xbuf, "https://"))) {
      *cp++ = '/';              /* for lynx proxy https, forward edited hdr */
      strcpy(cp, pp);           /* delete https://x.y.z:p/ */
      SSL_write(ssl_conn, xbuf, strlen(xbuf));
    }
    do {
      FD_ZERO(&fds);
      FD_SET(rnet, &fds);
      FD_SET(acpt, &fds);
      select(1 + (rnet > acpt ? rnet : acpt), &fds, NULL, NULL, NULL);
      if (FD_ISSET(rnet, &fds) && (n = SSL_read(ssl_conn, xbuf, 4096)) > 0)
        write(acpt, xbuf, n);
      if (FD_ISSET(acpt, &fds) && (n = read(acpt, xbuf, 4096)) > 0)
        SSL_write(ssl_conn, xbuf, n);
    } while (n > 0);
    SSL_free(ssl_conn);
  }
}
