/* lxp.c: a program to proxy SSL for Lynx using SSLeay */
/* remember to setenv https_proxy=http://127.0.0.1:5010/ */
/* and snews_proxy=nntp://127.0.0.1:5010/ */
/* gcc -O6 -I/usr/local/ssl/include lxp.c -lssl -lcrypto -s -o lxp */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <resolv.h>
#include "ssl.h"
#define XBUFSIZ 4096

int                 main(int argc, char *argv[])
{
  SSL_CTX            *ssl_ctx;
  SSL                *ssl_conn;
  int                 n, flag;
  int                 lstn, acpt = -1, rnet = -1;
  fd_set              fds;
  struct sockaddr_in  sin, rsin;
  struct hostent     *host;
  char               *cp, *pp;
  char                dns[128];
  char                xbuf[XBUFSIZ];

  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);
  bind(lstn, (struct sockaddr *) &sin, sizeof(sin));
  ssl_ctx = SSL_CTX_new();
  X509_set_default_verify_paths(ssl_ctx->cert);
  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], XBUFSIZ - 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_conn = SSL_new(ssl_ctx);
    SSL_set_fd(ssl_conn, rnet);
    if ((n = SSL_connect(ssl_conn)) < 0)
      continue;
    if( (cp = strstr(xbuf, "https://") )) {
      *cp++ = '/'; /* for lynx proxy https, forward edited header */
      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, XBUFSIZ)) > 0)
	  write(acpt, xbuf, n);
      if (FD_ISSET(acpt, &fds) && (n = read(acpt, xbuf, XBUFSIZ)) > 0)
	  SSL_write(ssl_conn, xbuf, n);
    } while( n > 0 );
    SSL_free(ssl_conn);
  }
}
