/*********************************************************************\
*  Copyright (c) 1991 by Wen-King Su (wen-king@vlsi.cs.caltech.edu)   *
*                                                                     *
*  You may copy or modify this file in any manner you wish, provided  *
*  that this notice is always included, and that you hold the author  *
*  harmless for any loss or damage resulting from the installation or *
*  use of this software.                                              *
\*********************************************************************/


/****************************************************************
 * FindPort -- hack of fver to find valid ports on a FSP server *
 * by Cuda.  Yes, it's ugly, but it works for me.               *
 * Type "fspscan -h" to see how it is used.                     *
 ****************************************************************/

#include "fspscan.h"
#include <netdb.h>

char *host = NULL;
char *outputfile = NULL;
/* Default values */
int  localport = 9191; 
int  remoteport = 21;
int  retries = 3;
int  client_trace = 0;
int  errno;

static int myfd;
static struct sockaddr_in server_addr;
static unsigned short myseq = 0;
static unsigned short key;
int client_intr_state = 0;

static struct sockaddr_in INET_ZERO = { AF_INET };

#define DSIZE (sizeof(int)*8)
#define SAVE(A) { int sav; sav = errno; A; errno = sav; }

long inet_addr();

main(argc,argv)
int argc;
char **argv;
{
    int  i;
    UBUF *ub;
    FILE *logfile;
    extern char *optarg;

    while ((i = getopt(argc, argv, "htp:o:r:l:i:")) != EOF) {
	switch(i) {
	case 'h':
	    printhelp(argc, argv);
	    break;
        case 't':
	    client_trace=1;
	    break;
        case 'r':
	    retries=atoi(optarg);
	    break;
	case 'p':
	    remoteport=atoi(optarg);
	    break;
	case 'l':
	    localport=atoi(optarg);
	    break;
	case 'i':
	    host = (char *) malloc(strlen(optarg)+1);
	    strcpy(host, optarg);
	    break;
        case 'o':
	    outputfile = (char *) malloc(strlen(optarg)+1);
	    strcpy(outputfile, optarg);
	    break;
	case '?':
	    printhelp(argc, argv);
	    break;
	default:
	    fprintf(stderr,"This error should not happen\n");
	}
    }
        
    if (host == NULL) {
        fprintf(stderr, "host/ip not specified, unable to continue\n");
	printhelp(argc, argv);
	exit(-1);
    }

    if (outputfile == NULL) {
	logfile=stdout;
    } else {
        logfile=fopen(outputfile,"a+");
    }

    fprintf(logfile,"Scanning %s\n",host);

    for ( ; ; ++remoteport) {
        init_client(host,remoteport,localport);
        signal(SIGINT,client_intr);

        ub = client_interact(CC_VERSION,0L, 0,NULLP, 0,NULLP);
	if (ub)
	    /* success! */
            fprintf(logfile,"Found FSP ver: %s on port %0d\n",
			    ub->buf,remoteport);
        else
	    fprintf(logfile,"%0d...nada\n",remoteport); 
        fflush(logfile);
        fdclose();
    }
}

printhelp(argc,argv)
int argc;
char **argv;
{
    fprintf(stderr,"%s - an fsp port scanner\n",argv[0]);
    fprintf(stderr,"Usage: %s [-h]\n",argv[0]);
    fprintf(stderr,"       (prints this help screen)\n");
    fprintf(stderr,"       %s [-t][-o file][-r retries][-l local port][-i ip address][-p starting port]\n\n",argv[0]);
    fprintf(stderr,"Defaults: (-o) file = stdout\n");
    fprintf(stderr,"          (-l) local port = 9191\n");
    fprintf(stderr,"          (-p) starting port = 21\n");
    fprintf(stderr,"          (-r) retries = 3\n");
    fprintf(stderr,"          (-t) trace = off\n");
    fprintf(stderr,"          (-i) host-ip = none (host MUST be specified)\n");
    exit(0);
}


UBUF *client_interact(cmd,pos,l1,p1,l2,p2)
    unsigned cmd, l1, l2;
    unsigned long pos;
    unsigned char *p1, *p2;
{
    struct sockaddr_in from;
    UBUF sbuf;
    static UBUF rbuf;
    unsigned char *s, *t, *d;
    unsigned u, n, sum, mask, mlen;
    int retval, bytes, retry;

    sbuf.cmd = cmd;
    sbuf.len = htons(l1);
    sbuf.pos = htonl(pos);

    client_intr_state = 1;

    for(u = l1, d = (unsigned char *) sbuf.buf; u--; *d++ = *p1++);
    for(u = l2				      ; u--; *d++ = *p2++);
    mlen = d - (unsigned char *) &sbuf;

    for(retry = 0; retry < retries; retry++) {
	sbuf.key = key;
	sbuf.seq = myseq;
	sbuf.sum = 0;

	for(t = (unsigned char *) &sbuf, sum = n = mlen; n--; sum += *t++);
	sbuf.sum = sum + (sum >> 8);
	if(client_trace && retry) 
	    write(2,"R",1);

	if(sendto(myfd,&sbuf,mlen,0,&server_addr,sizeof(server_addr)) == -1)
						{ perror("sendto"); exit(1); }
	mask = 1 << myfd;

	while(1) {
	    retval = _x_select(&mask, 3000L);

	    if((retval == -1) && (errno = EINTR)) continue;

	    if(retval == 1) {  /* an incoming message is waiting */
		bytes = sizeof(from);
		if((bytes = recvfrom(myfd,(char*)&rbuf,sizeof(rbuf),0,
					&from,&bytes)) < UBUF_HSIZE) continue;

		s = (unsigned char *) &rbuf;
		d = s + bytes;
		u = rbuf.sum; rbuf.sum = 0;
		for(t = s, sum = 0; t < d; sum += *t++);
		sum = (sum + (sum >> 8)) & 0xff;
		if(sum != u) continue;  /* wrong check sum */

		rbuf.len = htons(rbuf.len);
		rbuf.pos = htonl(rbuf.pos);

		if(rbuf.seq 	      != myseq) continue;  /* wrong seq # */
		if(rbuf.len+UBUF_HSIZE > bytes) continue;  /* truncated.  */

		myseq++; key = rbuf.key;	/* update seq and keys.   */

		if(client_intr_state == 2) { client_done(); exit(1); }

		return(&rbuf);

	    } else break;   /* go back to re-transmit buffer again */
	}
    }
    /* return NULL if there have been too many retries - ffindport hack */
    return(NULL);
}


init_client(host,port,myport)
    char *host;
    int   port;
    int myport;
{
    if((myfd = _x_udp(&myport)) == -1)
		{ perror("socket open"); exit(1); }

    if(_x_adr(host,port,&server_addr) == -1)
		{ perror("server addr"); exit(1); } 
}

client_done()
{
    (void) client_interact(CC_BYE,0L,0,NULLP,0,NULLP);
}

void client_intr()
{
    switch(client_intr_state) {
        case 0: exit(2);
        case 1: client_intr_state = 2; break;
        case 2: exit(3);
    }
}

fdclose()
{
    close(myfd);
}


#ifndef EXOS_IPC

_x_udp(port)
    int *port;
{
    int f, len, zz;
    struct sockaddr_in me ;
    struct sockaddr_in sin;

    me = sin = INET_ZERO;

    me.sin_port = htons((unsigned short) *port);
    me.sin_family = AF_INET;
 
    if((f=socket(AF_INET,SOCK_DGRAM,0)) == -1) return(-1);
 
    if( setsockopt(f,SOL_SOCKET,SO_REUSEADDR,(int)&zz,sizeof(zz)) < 0 ||
        bind(f,(struct sockaddr *) &me,(len = sizeof(me))) < 0 ||
        getsockname(f,(char *)&sin,&len) < 0)
                                { SAVE(((void) close(f))); return(-1); }
    if(!*port) *port = ntohs((unsigned short) sin.sin_port); return(f);
}      

_x_adr(host,port,his)
    struct sockaddr_in *his;
    char *host;
    int port;
{
    char myhost[128];
    struct hostent *H;
    int    i;
    char *s, *d;
 
    *his = INET_ZERO;
    if(!host) (void) gethostname(host = myhost,sizeof(myhost));
 
    if((his->sin_addr.s_addr = inet_addr(host)) != -1) {   
	his->sin_family = AF_INET;
    } else
    if(H = gethostbyname(host)) {   
	for(s = (char *)H->h_addr, 
	    d = (char *)&his->sin_addr, 
	    i = H->h_length; i--; *d++ = *s++);
        his->sin_family = H->h_addrtype;
    } else return(-1);
    his->sin_port = htons((unsigned short) port);
 
    return(0);
}

_x_select(rf, tt)       /* tt is in unit of ms */
    int *rf;
    long tt;
{
    struct timeval timeout;
 
    if(tt != -1)
    {
        timeout.tv_sec  =  tt / 1000;
        timeout.tv_usec = (tt % 1000)*1000;
        return(select(DSIZE, rf, (int *) 0, (int *) 0, &timeout));
    }
       
    return(select(DSIZE, rf, (int *) 0, (int *) 0, (struct timeval *) 0));
}
#endif  /* not EXOS_IPC */


#ifdef EXOS_IPC

extern long rhost();

_x_udp(port)
    int *port;
{
    struct sockaddr_in sin; int f;

    sin = INET_ZERO;
    sin.sin_family = AF_INET;
    sin.sin_port   = htons((unsigned short) *port);
    if((f = socket(SOCK_DGRAM, (struct sockproto *) 0, &sin, SO_REUSEADDR))
							== -1) return(-1);
    sin = INET_ZERO;
    if(socketaddr(f,&sin) == -1) { SAVE(((void) close(f))); return(-1); }
    if(!*port) *port = ntohs((unsigned short) sin.sin_port); return(f);
}

_x_adr(host,port,his)
    char *host;
    int port;
    struct sockaddr_in *his;
{
    char myhost[128];
    int f;

    *his = INET_ZERO;
    if(!host) (void) gethostname(host = myhost,sizeof(myhost));

    his->sin_family = AF_INET;
    his->sin_port   = htons((unsigned short) port);

    if((his->sin_addr.s_addr = rhost(&host)) == -1) return(-1);

    return(0);
}

_x_select(readfds, tt)
    int *readfds;
    long tt;				/* Time to wait in miniseconds. */
{
    int  code;
    long mask = *readfds;

    if(tt & 0xc0000000) tt = 0x3fffffff;/* It does not like 0x7fffffff. */

    code = select(DSIZE, &mask, (long *) 0, tt);

    *readfds = mask;

    return(code);
}

recvfrom(s, msg, len, flags, from, fromlen)
    char *msg;
    int s, len, flags, *fromlen;
    struct sockaddr_in *from;
{
    return(receive(s,from,msg,len));
}

sendto(s, msg, len, flags, to, tolen)
    char *msg;
    int s, len, flags, tolen;
    struct sockaddr_in *to;
{
     to->sin_family = AF_INET;
     return(send(s,to,msg,len));
}

#endif /* EXOS_IPC */


