/*

$Header: socket.c[1.17] Wed Sep  9 16:23:14 1992 nickel@cs.tu-berlin.de proposed $
This file is part of socket(1).
Copyright (C) 1992 by Juergen Nickelsen <nickel@cs.tu-berlin.de>
Please read the file COPYRIGHT for further details.

*/

#include <sys/types.h>
#include <sys/socket.h>
#ifdef USE_INET6
#include <sys/time.h>
#endif /* USE_INET6 */
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#ifdef SEQUENT
#include <strings.h>
#else
#include <string.h>
#endif
#include <stdlib.h>
#include <unistd.h>
#include "globals.h"

/* global variables */
int forkflag = 0 ;		/* server forks on connection */
int serverflag = 0 ;		/* create server socket */
int loopflag = 0 ;		/* loop server */
int verboseflag = 0 ;		/* give messages */
int readonlyflag = 0 ;		/* only read from socket */
int writeonlyflag = 0 ;		/* only write to socket */
int quitflag = 0 ;		/* quit connection on EOF */
int crlfflag = 0 ;		/* socket expects and delivers CRLF */
int backgflag = 0 ;		/* put yourself in background */
int active_socket ;		/* socket with connection */
char *progname ;		/* name of the game */
char *pipe_program = NULL ;	/* program to execute in two-way pipe */

#ifndef USE_INET6
void server A((int port, char *service_name)) ;
void client A((char *host, int port, char *service_name)) ;
#else /* USE_INET6 */
void server A((char *port)) ;
void client A((char *host, char *port)) ;
#endif /* USE_INET6 */

void handle_server_connection A((void)) ;
extern void init_signals A((void)) ;
extern void do_io A((void)) ;

int main(argc, argv)
int argc ;
char **argv ;
{
    char *cp ;			/* to point to '/' in argv[0] */
    int opt ;			/* option character */
    int error = 0 ;		/* usage error occurred */
    extern int optind ;		/* from getopt() */
    /* char *host ; */		/* name of remote host */
#ifndef USE_INET6
    int port ;			/* port number for socket */
    char *service_name ;	/* name of service for port */
#endif /* USE_INET6 */

    /* print version ID if requested */
    if (argv[1] && !strcmp(argv[1], "-version")) {
	puts(so_release()) ;
	exit(0) ;
    }

    /* set up progname for later use */
    progname = argv[0] ;
    cp = strrchr(progname, '/');
    if (cp) progname = cp + 1 ;

    /* parse options */
    while ((opt = getopt(argc, argv, "bcflp:qrsvw?")) != -1) {
	switch (opt) {
	  case 'f':
	    forkflag = 1 ;
	    break ;
	  case 'c':
	    crlfflag = 1 ;
	    break ;
	  case 'w':
	    writeonlyflag = 1 ;
	    break ;
	  case 'p':
	    pipe_program = argv[optind - 1] ;
	    break ;
	  case 'q':
	    quitflag = 1 ;
	    break ;
	  case 'r':
	    readonlyflag = 1 ;
	    break ;
	  case 's':
	    serverflag = 1 ;
	    break ;
	  case 'v':
	    verboseflag = 1 ;
	    break ;
	  case 'l':
	    loopflag = 1 ;
	    break ;
	  case 'b':
	    backgflag = 1 ;
	    break ;
	  default:
	    error++ ;
	}
    }
    if (error ||		/* usage error? */
	argc - optind + serverflag != 2) { /* number of args ok? */
	usage() ;
	exit(15) ;
    }

    /* check some option combinations */
#define senseless(s1, s2) \
    fprintf(stderr, "It does not make sense to set %s and %s.\n", (s1), (s2))

    if (writeonlyflag && readonlyflag) {
	senseless("-r", "-w") ;
	exit(15) ;
    }
    if (loopflag && !serverflag) {
	senseless("-l", "not -s") ;
	exit(15) ;
    }
    if (backgflag && !serverflag) {
 	senseless("-b", "not -s") ;
 	exit(15) ;
    }
    if (forkflag && !serverflag) {
	senseless("-f", "not -s") ;
    }
 
    /* set up signal handling */
    init_signals() ;

#ifndef USE_INET6
    /* get port number */
    port = resolve_service(argv[optind + 1 - serverflag],
			   "tcp", &service_name) ;
    if (port < 0) {
	fprintf(stderr, "%s: unknown service\n", progname) ;
	exit(5) ;
    }

#endif /* not USE_INET6 */
    /* and go */
    if (serverflag) {
	if (backgflag) {
	    background() ;
	}
#ifndef USE_INET6
	server(port, service_name) ;
#else /* USE_INET6 */
	server(argv[optind]) ;
#endif /* USE_INET6 */
    } else {
#ifndef USE_INET6
	client(argv[optind], port, service_name) ;
#else /* USE_INET6 */
	client(argv[optind], argv[optind + 1]) ;
#endif /* USE_INET6 */
    }	       
    exit(0) ;
}


#ifndef USE_INET6
void server(port, service_name)
int port ;
char *service_name ;
#else /* USE_INET6 */
void server(port)
char *port ;
#endif /* USE_INET6 */
{
#ifndef USE_INET6
    int socket_handle, alen ;
#else /* USE_INET6 */
    int *socket_handle_list ;
#endif /* USE_INET6 */

    /* allocate server socket */
#ifndef USE_INET6
    socket_handle = create_server_socket(port, 1) ;
    if (socket_handle < 0) {
#else /* USE_INET6 */
    socket_handle_list = create_server_sockets(&port, 1) ;
    if (!socket_handle_list) {
#endif /* USE_INET6 */
	perror2("server socket") ;
	exit(1) ;
    }
#ifdef USE_INET6

#endif /* USE_INET6 */
    if (verboseflag) {
#ifndef USE_INET6
	fprintf(stderr, "listening on port %d", port) ;
	if (service_name) {
	    fprintf(stderr, " (%s)", service_name) ;
	}
	fprintf(stderr, "\n") ;
#else /* USE_INET6 */
	char *ip, *port;
	int fd, i;

	fd = socket_handle_list[1];
	for (i = 1; i <= socket_handle_list[0]; i++) {
    	    if (!fd || fd < socket_handle_list[i])
       	        fd = socket_handle_list[i];
	    socket_local_name (fd, NULL, &ip, &port);
	    fprintf(stderr, "listening at address %s on port %s\n", ip, port) ;
    	}
#endif /* USE_INET6 */
    }

    /* server loop */
    do {
#ifndef USE_INET6
	struct sockaddr_in sa ;
	    
	alen = sizeof(sa) ;

	/* accept a connection */
	if ((active_socket = accept(socket_handle,
			  (struct sockaddr *) &sa,
			  &alen)) == -1) {
	    perror2("accept") ;
	} else {
	    /* if verbose, get name of peer and give message */
	    if (verboseflag) {
		struct hostent *he ;
		long norder ;
		char dotted[20] ;

		he = gethostbyaddr((const char *)&sa.sin_addr.s_addr,
				   sizeof(sa.sin_addr.s_addr), AF_INET) ;
		if (!he) {
		    norder = htonl(sa.sin_addr.s_addr) ;
		    sprintf(dotted, "%d.%d.%d.%d",
                           (int)((norder >> 24) & 0xff),
                           (int)((norder >> 16) & 0xff),
                           (int)((norder >>  8) & 0xff),
                           (int)(norder & 0xff)) ;
#else /* USE_INET6 */
	struct timeval tv;
	fd_set readfd;
    	int fd, i, nfds, retval;

	fd = socket_handle_list[1];
	for (i = 1; i <= socket_handle_list[0]; i++) {
    	    if (!fd || fd < socket_handle_list[i])
       	        fd = socket_handle_list[i];
    	}
	nfds=fd+1;

	FD_ZERO(&readfd);
	for (i = 1; i <= socket_handle_list[0]; i++)
	    FD_SET(socket_handle_list[i],&readfd);

    	tv.tv_sec  = 10;
    	tv.tv_usec = 0;

    	retval = select(nfds, &readfd, NULL, NULL, &tv);

    	if(retval!=-1) {
            for (i = 1; i <= socket_handle_list[0]; i++) {
		fd = socket_handle_list[i];
		if (FD_ISSET(fd, &readfd)) {

		    /* accept a connection */
		    active_socket = accept(fd, (struct sockaddr *) 0, (int *)0);
		    if (active_socket == -1) {
			perror2("accept") ;
		    } else {
			/* if verbose, get name of peer and give message */
			if (verboseflag) {
			    char *host, *ip, *port;

			    if(!socket_remote_name(active_socket,&host, &ip, &port)) {
				fprintf(stderr, "connection from %s at %s to %s\n", host, ip, port);
			    }
			    else
				fprintf(stderr, "cannot get name or address of peer") ;
			}

			if (forkflag) {
			    switch (fork()) {
			    case 0:
				handle_server_connection() ;
				exit(0) ;
			    case -1:
				perror2("fork") ;
				break ;
			    default:
				close(active_socket) ;
				wait_for_children() ;
			    }
			} else {
			    handle_server_connection() ;
			}
		    }
#endif /* USE_INET6 */
		}
#ifndef USE_INET6
		fprintf(stderr, "connection from %s\n",
			(he ? he->h_name : dotted)) ;
	    }
	    if (forkflag) {
		switch (fork()) {
		  case 0:
		    handle_server_connection() ;
		    exit(0) ;
		  case -1:
		    perror2("fork") ;
		    break ;
		  default:
		    close(active_socket) ;
		    wait_for_children() ;
		}
	    } else {
		handle_server_connection() ;
#endif /* not USE_INET6 */
	    }
	}
    } while (loopflag) ;
#ifdef USE_INET6

    free (socket_handle_list);
#endif /* USE_INET6 */
}


void handle_server_connection()
{
    /* open pipes to program, if requested */
    if (pipe_program != NULL) {
	open_pipes(pipe_program) ;
    }
    /* enter IO loop */
    do_io() ;
    /* connection is closed now */
    close(active_socket) ;
    if (pipe_program) {
	/* remove zombies */
	wait_for_children() ;
    }
}


#ifndef USE_INET6
void client(host, port, service_name)
#else /* USE_INET6 */
void client(host, port)
#endif /* USE_INET6 */
char *host ;
#ifndef USE_INET6
int port ;
char *service_name ;
#else /* USE_INET6 */
char *port ;
#endif /* USE_INET6 */
{
    /* get connection */
#ifndef USE_INET6
    active_socket = create_client_socket(&host, port) ;
#else /* USE_INET6 */
    active_socket = create_client_socket(&host, &port) ;
#endif /* USE_INET6 */
    if (active_socket == -1) {
	perror2("client socket") ;
	exit(errno) ;
    } else if (active_socket == -2) {
	fprintf(stderr, "%s: unknown host %s\n", progname, host) ;
	exit(13) ;
    }
    if (verboseflag) {
#ifndef USE_INET6
	fprintf(stderr, "connected to %s port %d", host, port) ;
	if (service_name) {
	    fprintf(stderr, " (%s)", service_name) ;
	}
	fprintf(stderr, "\n") ;
#else /* USE_INET6 */
	char *host, *ip, *port;

	socket_local_name (active_socket, &host, &ip, &port);
	fprintf(stderr, "connected to %s with address %s at port %s\n", host, ip, port) ;
#endif /* USE_INET6 */
    }

    /* open pipes to program if requested */
    if (pipe_program != NULL) {
	open_pipes(pipe_program) ;
    }
    /* enter IO loop */
    do_io() ;
    /* connection is closed now */
    close(active_socket) ;
}

/*EOF*/


syntax highlighted by Code2HTML, v. 0.9.1