/* Quake proxy */

#include <sys/types.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/wait.h>

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

extern char *optarg;
extern int optind;

#define VERSION "1.0"

#define MAX 4096
unsigned char       msg[MAX];
char *name;


void usage ()
{
   fprintf(stderr, "usage: %s [-l localport] [-r remoteport]", name);
   fprintf(stderr, " [-h] server\n");
}


int main(int argc, char **argv)
{
   int sfd, cfd, c2fd, n, ucl, i, pid, opt;
   fd_set fds;
   struct sockaddr_in cremote_addr, clocal_addr;
   struct sockaddr_in sremote_addr, slocal_addr;
   struct sockaddr_in kuk_addr;
   struct hostent *host;
   struct timeval tv, *tvp;
   int localport = 27000, remoteport = 27000, debug = 0;

   name = argv[0];

   while (opt = getopt(argc, argv, "l:r:hv"), opt != -1) {
       switch (opt) {
           case 'l':
               localport = atoi(optarg);
               break;

           case 'r':
               remoteport = atoi(optarg);
               break;

           case 'v':
               fprintf(stderr, "qudproxy version %s", VERSION);
               return 0;

           default:
               usage();
               return opt != 'h';
       }
   }

   if (optind != argc - 1) {
       usage();
       return 1;
   }

   ucl = sizeof(struct sockaddr_in);

   bzero(&sremote_addr, ucl);
   sremote_addr.sin_family = AF_INET;
   sremote_addr.sin_port = htons(remoteport);
   n = inet_addr(argv[optind]);
   if (n != -1)
       memcpy(&sremote_addr.sin_addr, &n, sizeof(n));
   else {
       host = gethostbyname(argv[optind]);
       if (host == NULL)
           return(-1);
       memcpy (&sremote_addr.sin_addr, host->h_addr, host->h_length);
   }

   bzero(&clocal_addr, ucl);
   clocal_addr.sin_family = AF_INET;
   clocal_addr.sin_port = htons(localport);

/* Surely there's a better way to determine our IP address.  */
   gethostname(msg, MAX);
   host = gethostbyname(msg);
   memcpy(&clocal_addr.sin_addr, host->h_addr, host->h_length);

   bzero(&slocal_addr, ucl);
   slocal_addr.sin_family = AF_INET;
   slocal_addr.sin_addr.s_addr = htonl(INADDR_ANY);

   cfd = socket(AF_INET, SOCK_DGRAM, 0);
   bind(cfd, (struct sockaddr *)&clocal_addr, ucl);

   sfd = socket(AF_INET, SOCK_DGRAM, 0);
   bind(sfd, (struct sockaddr *)&slocal_addr, ucl);

   while (1) {
       int i;

       FD_ZERO(&fds);
       FD_SET(sfd, &fds);
       FD_SET(cfd, &fds);

       if (0 >= select(1 + (cfd > sfd ? cfd : sfd), &fds, NULL, NULL, NULL))
           return 0;

       if (FD_ISSET(cfd, &fds)) {
           n = recvfrom(cfd, msg, MAX, 0,
                       (struct sockaddr *)&cremote_addr, &ucl);
           if (n == -1) {
               perror("qudproxy");
               return 1;
           }
           sendto(sfd, msg, n, 0, (struct sockaddr *)&sremote_addr, ucl);
       }

       if (FD_ISSET(sfd, &fds)) {
           n = recvfrom(sfd, msg, MAX, 0, NULL, &ucl);
           if (n == -1) {
               perror("qudproxy");
               return 1;
           }
           sendto(cfd, msg, n, 0, (struct sockaddr *)&cremote_addr, ucl);
       }
   }
}