#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include "MULTINET_COMMON_ROOT:[MULTINET.INCLUDE]NETDB.H"
#include "MULTINET_COMMON_ROOT:[MULTINET.INCLUDE.SYS]TYPES.H"
#include "MULTINET_COMMON_ROOT:[MULTINET.INCLUDE.SYS]SOCKET.H"
#include "MULTINET_COMMON_ROOT:[MULTINET.INCLUDE.SYS]TIME.H"
#include "MULTINET_COMMON_ROOT:[MULTINET.INCLUDE.NETINET]IN.H"
#include "MULTINET_COMMON_ROOT:[MULTINET.INCLUDE.ARPA]INET.H"
#include "MULTINET_COMMON_ROOT:[MULTINET.INCLUDE]ERRNO.H"
#include "MULTINET_COMMON_ROOT:[MULTINET.INCLUDE.VMS]INETIODEF.H"

#define  nentry(x)	(sizeof(x)/sizeof(x[0]))


/*
**  Attempt to connect to the specified server.  Return value is the
**  file descriptor of the socket or -1 if unable to connect.
*/

server(base,service,port,logflag)
char *base;		/* Server.u base name */
char *service;          /* Internet service name */
int  port;		/* Reserved port to use if non-zero */
int  logflag;		/* Debugging/logging flag */
{

  struct sockaddr_in sa_in;  /* Socket address in internet style */
  register struct hostent *host = 0;
  char server[256];

  int     i, len, sock, do_dgram_prep;
  char    buf[80];
  struct  servent *sp;
  struct  timeval timeout;
  fd_set  fds;

  /*
  **  These things don't change, putting them here makes it easier on
  **  us since they don't really need to be in /etc/services on every
  **  system in the world.
  */

  static struct {
    char *name;
    int  port;
  } services[] = { {"li", 605}, {"smart", 915}, {"tpop", 602} };

  /*
  **  First figure out what kind of server we're connecting to.  Some
  **  answer a datagram to tell us they're there, others we just have
  **  to try to connect to.  This table should be updated if the servers
  **  are updated.
  */

  static char *dgram_servers[] = { "li", "tms", "tpop" };

  do_dgram_prep = 0;
  for (i = 0; i < nentry(dgram_servers); i++) {
    if (!strcmp(service,dgram_servers[i])) {
      do_dgram_prep++;
      break;
    }
  }

  /*
  **  Figure out what service number were talking to.
  */

  sa_in.sin_port = 0;
  if (sp = getservbyname(service,"tcp")) {
    sa_in.sin_port = sp->s_port;
  } else {
    for (i = 0; i < nentry(services); i++) {
      if (!strcmp(service,services[i].name)) {
        sa_in.sin_port = htons(services[i].port);
        break;
      }
    }
  }

  if (!sa_in.sin_port) {
    fprintf(stderr,"Unknown network service: %s\n",service);
#ifndef VMS
    if (logflag & 0x80)
      syslog(LOG_ERR,"Unknown network service: %s",service);
#endif
    return(-2);
  }
 
  /*
  **  Attempt to connect to a server
  */
 
  if (do_dgram_prep) {
    if ((sock = socket(AF_INET,SOCK_DGRAM,0)) < 0) {
      socket_perror("socket");
      exit(1);
    }
  }

  i = 1;
  for (i = 1;; i++) {
    sprintf(server,"%s%d.u.washington.edu",base,i);
    if (host = gethostbyname(server)) {
      sa_in.sin_family = host->h_addrtype;
      bcopy(host->h_addr, (caddr_t)&sa_in.sin_addr, host->h_length);
    } else {
      if (i == 1) {
        fprintf(stderr,"%s: unknown host\n", server);
        return(-2);
      }
      break;
    }

    if (do_dgram_prep) {

      if (sendto(sock,"p\n",2,0,&sa_in,sizeof(sa_in)) != 2) {
        socket_perror("sendto");
        exit(1);
      }

    } else {

      if (logflag & 0x40) {
        unsigned char *s = (unsigned char *) &sa_in.sin_addr.s_addr;
      }
      if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        socket_perror("socket");
      } else {
        struct sockaddr_in sin;

        /*
        **  If the port is non-zero, we have to come in on a trusted
        **  port number less than 1024.
        */

        while (port > 0) {
          sin.sin_family = AF_INET;
          sin.sin_addr.s_addr = 0;
          sin.sin_port = htons((u_short)port);
          if (bind(sock,(caddr_t)&sin,sizeof(sin)) < 0) {
            if (socket_errno == EADDRINUSE || socket_errno == EADDRNOTAVAIL) {
              port--;
            } else {
	      fprintf(stderr,"Port %d ",port);
	      socket_perror("bind");
              return(-2);
            }
          } else {
            break;
          }
        }

        /*
        **  Do the actual connection.
        */

        if (connect(sock, (struct sockaddr *)&sa_in, sizeof (sa_in)) < 0) {
          if (logflag & 0x40)
            socket_perror("connect");
        } else {
          break;
        }
        close(sock);
      }
      sock = -2;

    }

  }

  if (do_dgram_prep) {

    /*
    **  Now check to see if we got any "bytes".
    */

    len = sizeof(sa_in);
    do {
      FD_ZERO(&fds);
      FD_SET(sock,&fds);
      timeout.tv_sec = 10;
      timeout.tv_usec = 0;
      if (!select(sock+1,&fds,0,0,&timeout)) {
        close(sock);
        return(-1);
      }
      recvfrom(sock,buf,sizeof(buf),0,&sa_in,&len);
    } while (*buf != 'p' && *buf != 'P');
    close(sock);
    if (logflag & 0x40) {
      unsigned char *s = (unsigned char *) &sa_in.sin_addr.s_addr;
      fprintf(stderr,"Trying %s connect to %d.%d.%d.%d.\n",
                                        service,s[0],s[1],s[2],s[3]);
    }
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
      socket_perror("socket");
      return(-2);
    }
    if (connect(sock, (struct sockaddr *)&sa_in, sizeof (sa_in)) < 0) {
      socket_perror("connect");
      return(-2);
    }

  }

  return(sock);

}
