/*
 * WhereAreYou.c	Where are you ? (search IP addresses)
 * Copyright(C)1997 by Hiroaki Sengoku <sengoku@gcd.org>
 * Version 1.0  Jan 26, 1997
 *
 * 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, 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 GNU Emacs; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Usage: whereareyou <host_begin> <host_end> <port> <password>
 *
 * Search the host on which `iamhere <port> <password>' are running.
 */

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

#define BUFMAX		20
#define TIMEOUT	       	10	/* [sec] */
#define NPACKETS	10
#define NTRY		6

int timeout = 0;

static void handler(sig,code)
int sig, code;
{
    switch(sig) {
      case SIGALRM:
	timeout = 1;
	break;
      default:
	exit(1);
    }
}

void host2addr(name,addrp,familyp)
char *name;
struct in_addr *addrp;
short *familyp;
{
    struct hostent *hp;
    if (hp=gethostbyname(name)) {
	bcopy(hp->h_addr,(char *)addrp,hp->h_length);
	if (familyp) *familyp = hp->h_addrtype;
    } else if ((addrp->s_addr=inet_addr(name)) != -1) {
	if (familyp) *familyp = AF_INET;
    } else {
	fprintf(stderr,"Unknown host : %s\n",name);
	exit(1);
    }
}

static void randpass(buf)
char *buf;
{
    int i;
    srand(time(NULL));
    for (i=0; i < BUFMAX; i++) {
	buf[i] = ' '+rand()%94;
    }
    buf[i] = '\0';
}

static int search(sd,sinp,begin,end,pass)
int sd;
struct sockaddr_in *sinp;
unsigned long begin, end;
char *pass;	/* password */
{
    struct sockaddr_in from;
    char buf[BUFMAX+1], salt[BUFMAX+1];
    unsigned char *p;
    int pid, from_len, len, i;
    unsigned long h;
    struct sigaction vec;
    int success = 0;
    randpass(buf);
    len = strlen(buf);
    memcpy(salt,buf,len);
    salt[0] = salt[0]%26 + 'A';	/* A-Z */
    salt[1] = salt[1]%26 + 'a';	/* a-z */
    salt[len] = '\0';
    for (i=0; i < len; i++) {
	pass[i] = (buf[i]^pass[i]^' ');
    }
    pass[i] = '\0';
    p = crypt(pass,salt);
    if (!(pid=fork())) {	/* child */
	for (i=0; i < NPACKETS; i++) {
	    for (h=begin; h <= end; h++) {
		sinp->sin_addr.s_addr = htonl(h);
		if (sendto(sd,buf,len,0,
			   (struct sockaddr*)sinp,sizeof(*sinp)) != len) {
		    fprintf(stderr,"Sendto failed.\n");
		}
	    }
	}
	exit(1);
    }
    vec.sa_handler = handler;
    vec.sa_mask = 0;
#ifdef SA_INTERRUPT
    vec.sa_flags = SA_INTERRUPT;
#else
    vec.sa_flags = 0;
#endif
    sigaction(SIGALRM,&vec,NULL);
    alarm(TIMEOUT);
    timeout = 0;
    while (!timeout) {
	from_len = sizeof(from);
	len = recvfrom(sd,buf,BUFMAX,0,
		       (struct sockaddr*)&from,&from_len);
	if (len > 0 && !memcmp(p,buf,len)) {
	    p = (unsigned char*)&from.sin_addr.s_addr;
	    printf("%d",*p++);
	    for (i=0; i < sizeof(h)-1; i++) {
		printf(".%d",*p++);
	    }
	    printf("\n");
	    success = 1;
	    break;
	}
    }
    kill(pid,SIGTERM);
    return success;
}

main(argc,argv)
int argc;
char *argv[];
{
    struct sockaddr_in sin;
    struct in_addr host_end;
    char pass[BUFMAX+1];
    int sd, len, i, done = 0;
#ifndef PASSWORD
    if (argc != 5) {
	fprintf(stderr,"Usage: whereareyou "
		"<host_begin> <host_end> <port> <password>\n");
	exit(1);
    }
#endif
    memset((char *)&sin,0,sizeof(sin));		/* clear sin struct */
    sin.sin_family = AF_INET;
#ifdef PASSWORD
    host2addr(HOST_BEGIN,&sin.sin_addr,&sin.sin_family);
    host2addr(HOST_END,&host_end,NULL);
    sin.sin_port = htons(PORT);
    strcpy(pass,PASSWORD);
#else
    host2addr(argv[1],&sin.sin_addr,&sin.sin_family);	/* host_begin */
    host2addr(argv[2],&host_end,NULL);			/* host_end */
    sin.sin_port = htons((u_short)atoi(argv[3]));	/* port */
    memset(pass,0,sizeof(pass));
    len = strlen(argv[4]);				/* password */
    if (len >= BUFMAX) len = BUFMAX;
    memcpy(pass,argv[4],len);
    pass[len] = '\0';
#endif
    if ((sd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0) {
	fprintf(stderr,"Can't get socket.\n");
	exit(1);
    }
    for (i=0; !done && i < NTRY; i++)
	done = search(sd,&sin,
		      ntohl(sin.sin_addr.s_addr),ntohl(host_end.s_addr),pass);
    exit(!done);
}

