/*
	Schrodinger's Box Haven  code version -0.1 alpha
        Copyright (C) 1994, Gordon Chan

	You can do whatever ya want with this code as long as you leave this
        copyright & disclaimer intact.  You can hack it up all you want, etc...
        No guarantees.

	Send comments, flames, bug reports, etc ... to
		au233@freenet.carleton.ca  <---- (preferred address for now)
		gjc@achilles.net
		gchan@ccs.carleton.ca
*/


/* Here's the copyrights & disclaimers from the Opium Den and unixCB codes */

/* Opium Den */
/*
  val's haven code v -0.85

        Copyright (C) 1994 Joel Ward

        Warranty:
                Don't blame me for anything.

        You can do whatever the fuck you want with this code as long as you
don't remove this copyright/disclaimer notice.
*/

/* unixCB */
/*
  
  cbd.c
  
  "cbd.c" is the main code for the CB simulator.
  
  Copyright (c) 1992, Gary Grossman.  All rights reserved.
  Send comments and questions to: garyg@soda.berkeley.edu
  
  */

#include<stdio.h>
#include<sys/socket.h>
#include<sys/time.h>
#include<sys/file.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<errno.h>
#include<fcntl.h>

#include "socket.h"
#include "main.h"


/* socket is initilised and binded to port on localhost and listens for 
   incoming connections */
/* init_socket returns the file descriptor of the server or -1 on error */

int init_socket (port)
	int port;
{
	struct sockaddr_in  server_address;
	struct hostent  *host_local;
	int socketfd, reuse_opt=1;
	char hostname[100];

	bzero (&server_address, sizeof (struct sockaddr_in) );
	bzero (&host_local, sizeof (struct hostent) );

	if (gethostname (hostname, sizeof (hostname)) < 0) {
#ifdef DEBUG
		write_err ("error with gethostname\n");
#endif
		return -1;
	}

	host_local = gethostbyname (hostname);

	server_address.sin_family = host_local->h_addrtype;
	server_address.sin_addr.s_addr = htonl (INADDR_ANY);
	server_address.sin_port = htons (port);
	
	if ((socketfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
#ifdef DEBUG
		write_err ("socket could not be opened\n");
#endif
		return -1;
	}	
	
	if (setsockopt (socketfd, SOL_SOCKET, SO_REUSEADDR, &reuse_opt, sizeof(reuse_opt)) < 0) {
#ifdef DEBUG
		write_err ("error with setting SO_REUSEADDR option\n");
#endif
		return -1;
	}

	if (bind (socketfd, (struct sockaddr *) &server_address, sizeof (struct sockaddr_in)) < 0) {		
#ifdef DEBUG
		write_err ("server could not be binded to socket\n");
#endif
		return -1;
	}

	if (listen (socketfd, 5) < 0) {
#ifdef DEBUG
		write_err ("could not listen\n");
#endif
		return -1;
	}

	if (fcntl (socketfd, F_SETFL, O_NONBLOCK) == -1) {
#ifdef DEBUG
		write_err ("server could not be set for non-blocking\n");
#endif
		return -1;
	}
 
	return socketfd;
}


/* remote connections to server are handled */
/* new_connect returns file descriptor of new user or -1 on error */

int new_connect (server_fd, remote_hostname, remote_port)
	int server_fd, *remote_port;
	char *remote_hostname;
{
	struct sockaddr_in  client_address;
	struct hostent  *host_remote;
	int  new_fd, client_length;

	client_length = sizeof (struct sockaddr_in);
	getsockname (server_fd, (struct sockaddr *) &client_address, &client_length);
	if ( (new_fd = accept (server_fd, (struct sockaddr *) &client_address, &client_length)) < 0)  {
		if (errno == EWOULDBLOCK) {	
			return -1;
		}
	} 
	fcntl (new_fd, F_SETFL, O_NONBLOCK);  

	if ( (host_remote = gethostbyaddr ( (char*) &client_address.sin_addr, sizeof (struct in_addr) , AF_INET)) < 0) {
#ifdef DEBUG
		write_err ("remote host address could not be resolved\n");
#endif
		sprintf (remote_hostname , "%d.%d.%d.%d", 
			((unsigned char *) (&client_address.sin_addr))[0], 
			((unsigned char *) (&client_address.sin_addr))[1], 
			((unsigned char *) (&client_address.sin_addr))[2], 
			((unsigned char *) (&client_address.sin_addr))[3] ); 
	} else {
		strcpy (remote_hostname, host_remote->h_name);
 	}
	*remote_port = ntohs(client_address.sin_port);  

	return (new_fd);
}
