/*
** ibench.c - a small benchmarking/stress-testing program for IDENT servers.
**
** Copyright (c) 1997 Peter Eriksson <pen@lysator.liu.se>
**
** This program is free software; you can redistribute it and/or
** modify it as you wish - as long as you don't claim that you wrote
** it.
**
** 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.
**
**
** Please note that this program *should* be run on the same machine
** as the IDENT server you wish to test. It assumes it can connect to
** it via the loopback interface.
**
** If you specify a server address using the -rADDR option, then
** the lookups will be for the *servers* username, and not the
** username "ibench" runs as.
*/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <pwd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static struct sockaddr_in host_sin;
static char *username;
static int rev_flag = 0;


static int
run_test(void)
{
    int fd, len, val;
    struct sockaddr_in our, sin;
    char buf[1024], buf2[1024];
    

    sin = host_sin;

    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
	perror("socket");
	exit(1);
    }
    
    if (connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0)
    {
	perror("connect");
	exit(1);
    }

    len = sizeof(our);
    if (getsockname(fd, (struct sockaddr *) &our, &len) < 0)
    {
	perror("getsockname");
	exit(1);
    }

    if (rev_flag)
	sprintf(buf, "%d , %d\r\n", ntohs(sin.sin_port), ntohs(our.sin_port));
    else
	sprintf(buf, "%d , %d\r\n", ntohs(our.sin_port), ntohs(sin.sin_port));
    
    if (write(fd, buf, strlen(buf)) < 0)
    {
	perror("write");
	exit(1);
    }

    if (shutdown(fd, 1) < 0)
    {
	perror("shutdown");
	exit(1);
    }
    
    len = read(fd, buf, sizeof(buf));
    if (len < 0)
    {
	perror("read");
	exit(1);
    }

    close(fd);
    
    buf[len] = '\0';
    len = sscanf(buf, " %*d , %*d : USERID : %*s : %s", buf2);

    if (len != 1)
    {
	fprintf(stderr, "Malformed reply (%d): %s\n", len, buf);
	exit(1);
    }

    if (strcmp(buf2, username) != 0)
    {
	fprintf(stderr, "Incorrect username: %s != %s\n", buf2, username);
	exit(1);
    }

    return 0;
}


int
main(int argc, char *argv[])
{
    int i, test_len, len, ntests;
    time_t start, stop;
    struct passwd *pp;
    

    if (argc < 2)
    {
	fprintf(stderr,
		"Usage: ibench [-rADDR] <seconds> [<port>] [<username>]\n");
	exit(1);
    }

    memset(&host_sin, 0, sizeof(host_sin));
    host_sin.sin_family = AF_INET;
    
    
    if (argc > 1 && strncmp(argv[1], "-r", 2) == 0)
    {
	rev_flag = 1;
	host_sin.sin_addr.s_addr = inet_addr(argv[1]+2);
	argv++;
	argc--;
    }
    else
	host_sin.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    if (argc > 3)
	username = argv[3];
    else
    {
	pp = getpwuid(getuid());
	username = pp->pw_name;
    }
    
    if (argc > 2)
	host_sin.sin_port = htons(atoi(argv[2]));
    else
	host_sin.sin_port = htons(113);

    test_len = atoi(argv[1]);

    time(&start);
    ntests = 0;
    
    do
    {
	run_test();
	++ntests;
	time(&stop);
    }
    while (stop - start < test_len);

    len = stop-start;
    if (len == 0)
	len = 1;
    
    printf("Test OK: %d requests in %d seconds (%d requests/s)\n",
           ntests, len, ntests/len);
    
    return 0;
}
