/* xscan.c
 *  scan a range of ip addresses or list of hosts for machines
 *  that allow us to connect to the X server. if you don't know
 *  what to do when you find one, bleh.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <X11/X.h>
#include <X11/Xlib.h>

#define UL unsigned long int

int i, j, k;
char ch;
char *line;
UL s_ip, e_ip;
Display *d;

/* test to see if we can connect to the specified address
   at the specified port (addr and port are network byte 
   order */
int connects(UL addr, unsigned short int port) {

  /* stolen from Samba and slightly modified (for the connect timeout) */
  int sys_select(fd_set *fds,struct timeval *tval)
  {
    struct timeval t2;
    int selrtn;
    do {
      if (tval) memcpy((void *)&t2,(void *)tval,sizeof(t2));
      errno = 0;
      selrtn = select(255,fds,NULL,NULL,tval?&t2:NULL);
    } while (selrtn<0 && errno == EINTR);
    return(selrtn);
  }

  /* also stolen from Samba and slightly modified (also for the connect timeout) */
  void msleep(int t)
  {
    int tdiff=0;
    struct timeval tval,t1,t2;  
    fd_set fds;
    gettimeofday(&t1, NULL);
    gettimeofday(&t2, NULL);
    while (tdiff < t) {
      tval.tv_sec = (t-tdiff)/1000;
      tval.tv_usec = 1000*((t-tdiff)%1000);
      FD_ZERO(&fds);
      errno = 0;
      sys_select(&fds,&tval);
      gettimeofday(&t2, NULL);
      tdiff = (t2.tv_sec - t1.tv_sec)*1000 - ((int)t2.tv_sec - (int)t1.tv_sec)/1000;
    }
  }

  /* this is my code now */
  struct sockaddr_in sin;
  int s;
  /* dunno why this is 250 i think something is broken */
  int connect_loop = 500;
  /* 6 second timeout */
  int loops = 6;
  int ret;
  char remotedisplay[20];
  
  printf("Trying %s...", inet_ntoa(addr));
  fflush(stdout);

  s = socket(AF_INET, SOCK_STREAM, 0);
  bzero((caddr_t)&sin, sizeof(sin));
  sin.sin_family = AF_INET;
  bind(s, (struct sockaddr *)&sin, sizeof(sin));
  sin.sin_port = port;
  bcopy(&addr, (char *)&sin.sin_addr, sizeof(addr));

  /* set non-blocking mode so connect() doesn't wait */
  fcntl (s, F_SETFL, O_NONBLOCK);
  
connect_again: 
  ret = connect(s, (struct sockaddr *)&sin, sizeof(sin));
 
  if(ret < 0 && (errno == EINPROGRESS || errno == EALREADY) && loops--) {
    msleep(connect_loop);
    goto connect_again;
  } 
  
  if((ret < 0) && (errno == EINPROGRESS || errno == EALREADY)) {
    printf("Connect Timeout\n");
    close(s);
    return 0;
  }
  if(ret < 0) {
    perror((char *)NULL);
    close(s);
    return 0;
  }
  /* if we made it this far it must have connected... close connection &
   *  try to open display */
  close(s);
  printf("TCP connection established...");
  strcpy(remotedisplay, (char *)inet_ntoa(addr));
  strcat(remotedisplay, ":0.0");
  if((d=XOpenDisplay(remotedisplay)) == NULL) {
    printf("X11 Connection failed.\n");
    return 0;
  }
  printf("X11 connection established!\n");
  XCloseDisplay(d);
  return 1;
}

int scan_stdin(unsigned short int port) {
  char host[128];
  FILE *logfile;
  struct hostent *hp;
  UL laddr;
  
  /* put all successful connections in xscanlog */
  if((logfile = fopen("xscanlog", "a+")) == NULL) {
    perror("xscan: fopen");
    exit(1);
  }
  while(strcmp(fgets(host, 128, stdin), "\n") != 0) {
    /* fuck the newline */
    host[strlen(host)-1] = 0;
    hp = gethostbyname(host);
    if(hp != NULL) {
      bcopy(hp->h_addr, &laddr, sizeof(laddr));
      if(connects(laddr, port)) {
        fprintf(logfile, "%s\n", host);
        fflush(logfile)
      }
    } else {
      printf("%s: Host not found\n", host);
    }
  }
  close((int)logfile);
  exit(0);
}

int scan_ips(UL sip, UL eip, unsigned short int port) {
  unsigned char startip[3], endip[3];
  FILE *logfile;
  unsigned long ip;
  
  /* put all successful connections in xscanlog */
  if((logfile = fopen("xscanlog", "a+")) == NULL) {
    perror("xscan: fopen");
    exit(1);
  }
  bcopy(&sip, startip, sizeof(sip));
  bcopy(&eip, endip, sizeof(eip));
  /* loops though all the ip's */
  printf("scanning %s to %s on port %i\n", inet_ntoa(sip), inet_ntoa(eip), ntohs(port));
  for(ip=ntohl(sip); ip<=ntohl(eip); ip++) {
    /* printf ("Trying %s...\n", inet_ntoa(htonl(ip))); */
    if(connects(htonl(ip), port)) {
      fprintf(logfile, "%s\n", inet_ntoa(ip));
      fflush(logfile);
    }
  }  
  close((int)logfile);
  return 0;
}

void usage() {
  printf("Usage: xscan [start_ip [end_ip]]|[-s]\n");
  printf("  start_ip is the IP address to begin the scan with\n");
  printf("  end_ip is the IP address to end the scan with\n");
  printf("  if end_ip is omitted, only 1 host will be checked\n");
  printf("  if the only argument is -s, hosts are read from stdin\n");
  exit(1);
 }

void main(int argc, char **argv) {
  printf("xscan v1.1      X11 connection scanner     xygorf 1997\n");
  if (argc == 2) {
    if(!strncmp(argv[1], "-s", 2)) {
      scan_stdin(htons(6000));
    }
  }
  if (argc != 3) {
    usage();
  }
  scan_ips(inet_addr(argv[1]), inet_addr(argv[2]), htons(6000));
  return;
}
