/******************************************************************** * wilkinson * 3.19VMS * 1995/05/25 14:00 * gopher_root1:[gopher.g2.vms2_13.object]Sockets.c,v * Exp * * Paul Lindner, University of Minnesota CIS. * * Copyright 1991, 1992 by the Regents of the University of Minnesota * see the file "Copyright" in the distribution for conditions of use. ********************************************************************* * MODULE: Sockets.c * Socket functions ********************************************************************* * Revision History: * Sockets.c,v * Revision 3.19VMS 1995/05/25 14:00 wilkinson * Consolodate VMS/Unix source code for server as well as client * * Revision 3.19 1994/07/31 05:09:47 lindner * Add option to log only IP * * Revision 3.18 1994/04/25 03:46:03 lindner * Fix for NETLIB and CMUIP * * Revision 3.17 1994/04/25 03:36:59 lindner * Modifications for Debug() and mismatched NULL arguments, added Debugmsg * * Revision 3.16 1994/04/08 20:05:53 lindner * gcc -Wall fixes * * Revision 3.15 1994/03/31 21:03:35 lindner * Use better, more descriptive return codes for socket routines * * Revision 3.14 1994/03/17 04:45:39 lindner * Add needed stdio.h * * Revision 3.13 1994/03/17 04:44:39 lindner * Add needed stdio.h * * Revision 3.12 1994/03/17 04:36:01 lindner * Move socket specific server code here, rearrange include files * * Revision 3.11 1994/03/08 15:56:20 lindner * gcc -Wall fixes * * Revision 3.10 1994/01/12 22:23:50 lindner * Fixes for Data General * * Revision 3.9 1993/12/27 16:16:03 lindner * Fix for sign on integer.. * * Revision 3.8 1993/10/19 20:49:02 lindner * Fix for NETLIB * * Revision 3.7 1993/10/11 17:26:01 lindner * Fix for cmuip/netlib * * Revision 3.6 1993/09/21 01:51:34 lindner * Moved netnames fcn.. * * Revision 3.5 1993/08/09 20:17:10 lindner * Fixes for CMULIB and NETLIB for VMS * * Revision 3.4 1993/08/05 03:23:37 lindner * Changes for CMUIP and NETLIB * * Revision 3.3 1993/07/29 20:01:02 lindner * Removed dead variables * * Revision 3.2 1993/07/27 05:30:27 lindner * Mondo Debug overhaul from Mitra * * Revision 3.1 1993/07/07 19:27:25 lindner * Socket functions * * * *********************************************************************/ /* Generic stuff */ #include "boolean.h" #include "compatible.h" #include "Debug.h" #ifdef unix # include #endif #include "Sockets.h" #include "Malloc.h" #include #ifdef VMS_SERVER #include char * vms_errno_string(); #include #include #endif /* * This turns the linger output off */ void SOCKlinger(sockfd, onoff) int sockfd; boolean onoff; { #if defined(SO_LINGER) && !defined(NO_LINGER) struct linger linger; linger.l_onoff = onoff; linger.l_linger = 0; if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof (linger)) < 0) #ifndef VMS_SERVER perror("server: can't turn off linger sockopt"),exit(-1); #else LOGGopher(-99,"fatal: can't turn off linger sockopt, %s", vms_errno_string()); #endif #endif } /* * This function returns a socket file descriptor bound to the given port */ #if !defined(CMUIP) && !defined(NETLIB) int SOCKbind_to_port(port) int port; { struct sockaddr_in serv_addr; int reuseaddr = 1; int sockfd; #ifdef VMS_SERVER union prvdef prvadr; unsigned long int ON = -1; if (port<1024) { bzero((char *) &prvadr, sizeof(prvadr)); prvadr.prv$v_sysprv = 1; if (SS$_NORMAL != (vaxc$errno = SYS$SETPRV (ON, &prvadr, 0, 0))) { LOGGopher(-1,"Can't insure SYSPRV for bind to port %d, %s", port, STRerror(vaxc$errno)); } } #endif if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) #ifndef VMS_SERVER perror("server: can't open stream socket"), exit(-1); #else LOGGopher(-99,"fatal: can't open stream socket, %s", vms_errno_string()); #endif /** Bind our local address so that the client can send to us **/ bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(port); if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddr, sizeof(reuseaddr)) < 0) #ifndef VMS_SERVER perror("server: can't set REUSEADDR!"),exit(-1); #else LOGGopher(-99,"fatal: can't set socket REUSEADDR, %s", vms_errno_string()); #endif if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) <0) #ifndef VMS_SERVER perror("server: can't bind local address"),exit(-1); #else LOGGopher(-99, "fatal: can't bind local address, %s", vms_errno_string()); #endif SOCKlinger(sockfd, FALSE); #ifdef VMS_SERVER bzero((char *) &prvadr, sizeof(prvadr)); prvadr.prv$v_sysprv = 1; if (SS$_NORMAL != (vaxc$errno = SYS$SETPRV (DEBUG, &prvadr, 0, 0))) { LOGGopher(-1,"Can't discard PRIVS, %s", STRerror(vaxc$errno)); } #endif return(sockfd); } /* * This routine finds out the hostname of the server machine. * It uses a couple of methods to find the fully qualified * Domain name * * If gdchost is non NULL then use that paramater instead of * the code in here. */ char * SOCKgetDNSname(backupdomain, gdchost) char *backupdomain; char *gdchost; { static char DNSname[MAXHOSTNAMELEN]; struct hostent *hp; if (*gdchost != '\0') return(gdchost); DNSname[0] = '\0'; /* Work out our fully-qualified name, for later use */ if (gethostname(DNSname, MAXHOSTNAMELEN) != 0) { #ifndef VMS_SERVER fprintf(stderr, "Cannot determine the name of this host\n"); exit(-1); #else LOGGopher(-99,"fatal: Cannot determine the name of this host"); #endif } /* Now, use gethostbyname to (hopefully) do a nameserver lookup */ hp = gethostbyname( DNSname); /* ** If we got something, and the name is longer than hostname, then ** assume that it must the the fully-qualified hostname */ if ( hp!=NULL && strlen(hp->h_name) > strlen(DNSname) ) strncpy( DNSname, hp->h_name, MAXHOSTNAMELEN ); else strcat(DNSname, backupdomain); return(DNSname); } /* * Tries to figure out what the currently connected port is. * * If it's a socket then it will return the port of the socket, * if it isn't a socket then it returns -1. */ int SOCKgetPort(fd) int fd; { struct sockaddr_in serv_addr; int length = sizeof(serv_addr); /** Try to figure out the port we're running on. **/ if (getsockname(fd, (struct sockaddr *) &serv_addr,&length) == 0) return(ntohs(serv_addr.sin_port)); else return(-1); } #endif /* not CMUIP nor NETLIB */ /* SOCKconnect performs a connection to socket 'service' on host * 'host'. Host can be a hostname or ip-address. If 'host' is null, the * local host is assumed. The parameter full_hostname will, on return, * contain the expanded hostname (if possible). Note that full_hostname is a * pointer to a char *, and is allocated by connect_to_gopher() * * Errors: ErrSocket* defined in Sockets.h * */ int SOCKconnect(hostname, port) char *hostname; int port; { #if !defined(CMUIP) && !defined(NETLIB) struct sockaddr_in Server; struct hostent *HostPtr; int sockfd = 0; unsigned int ERRinet = -1; #ifdef _CRAY ERRinet = 0xFFFFFFFF; /* -1 doesn't sign extend on 64 bit machines */ #endif /*** Find the hostname address ***/ if (hostname == NULL || *hostname == '\0') return(ErrSocketNullHost); #ifdef DGUX Server.sin_addr = inet_addr(hostname); if (Server.sin_addr.s_addr == ERRinet) #else if ((Server.sin_addr.s_addr = inet_addr(hostname)) == ERRinet) #endif { if ((HostPtr = gethostbyname(hostname)) != NULL) { bzero((char *) &Server, sizeof(Server)); bcopy(HostPtr->h_addr, (char *) &Server.sin_addr, HostPtr->h_length); Server.sin_family = HostPtr->h_addrtype; } else return (ErrSocketGetHost); } else Server.sin_family = AF_INET; Server.sin_port = (unsigned short) htons(port); /*** Open the socket ***/ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return (ErrSocketCall); #ifndef UCX setsockopt(sockfd, SOL_SOCKET, ~SO_LINGER, 0, 0); #endif setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0); setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, 0, 0); /*** Connect ***/ if (connect(sockfd, (struct sockaddr *) &Server, sizeof(Server)) < 0) { closenet(sockfd); return (ErrSocketConnect); } return(sockfd); #else /* !NETLIB && !CMUIP */ #ifdef NETLIB int status; static int iSock = 0; struct { long len; char *adr; } host_desc; #define NET_K_TCP 1 status = NET_ASSIGN (&iSock); if ((status & 1) == 0) return (ErrSocketCall); status = NET_BIND (&iSock, NET_K_TCP); if ((status & 1) == 0) return (ErrSocketCall); host_desc.adr = hostname; host_desc.len = strlen (host_desc.adr); status = TCP_CONNECT (&iSock, &host_desc, port); if ((status & 1) == 0) { NET_DEASSIGN (&iSock); if (status == SS$_REJECT || status == SS$_TIMEOUT) { if (status == SS$_REJECT) errno = ECONNREFUSED; else errno = ETIMEDOUT; return (ErrSocketConnect); } if (status == SS$_ENDOFFILE) return (ErrSocketGetHost); return (ErrSocketGetService); } return (iSock); #else /* ifdef NETLIB: assume CMUIP */ short channel; int status; struct { short status; short size; long xxx; } cmu_iosb; static struct {long l; char *a;} ip_dev = {12, "INET$DEVICE:"}; globalvalue NET$_CREF; /* Connection refused */ globalvalue NET$_FTO; /* Function timedout */ globalvalue NET$_DSNAMERR; /* Domain server name error */ status = SYS$ASSIGN (&ip_dev, &channel, 0, 0); if ((status & 1) == 0) return (ErrSocketCall); status = SYS$QIOW (0, channel, IO$_CREATE, &cmu_iosb, 0, 0, hostname, port, 0, 1, 0, 0); if ((status & 1) == 0 || (cmu_iosb.status & 1) == 0) { SYS$DASSGN (channel); if (cmu_iosb.status == SS$_ABORT || cmu_iosb.xxx == NET$_FTO) { if (cmu_iosb.xxx == NET$_CREF) errno = ECONNREFUSED; else errno = ETIMEDOUT; return(ErrSocketConnect); } if (cmu_iosb.xxx == NET$_DSNAMERR) return (ErrSocketGetHost); } return (channel); #endif #endif } /* * */ #if !defined(CMUIP) && !defined(NETLIB) /* temp - MLH */ int SOCKlisten(We) struct sockaddr_in * We; { int sockfd = 0; struct hostent *HostPtr; int len = sizeof(struct sockaddr); char name[100]; sockfd = SOCKbind_to_port(sockfd); if (listen(sockfd, 5) || getsockname(sockfd, (struct sockaddr *) We, &len)) { closenet(sockfd); return(ErrSocketGetHost); } gethostname(name, 100); if ((HostPtr = gethostbyname(name))) bcopy(HostPtr->h_addr, (char *) &We->sin_addr, HostPtr->h_length); return(sockfd); } /* SOCKaccept accepts a connection form some socket. * * Errors: ErrSocket* defined in Sockets.h */ int SOCKaccept(s, we) int s; struct sockaddr_in we; { int sockfd = 0; int len = sizeof(struct sockaddr); unsigned short tem; tem = ntohs(we.sin_port); Debugmsg("Here we go...\n"); if ((sockfd = accept(s, (struct sockaddr *) &we, &len)) < 0) { return ErrSocketConnect; } close(s); /* Der Mohr hat seine Schuldigkeit getan */ return(sockfd); } /* * SOCKnetnames -- return the network, subnet, and host names of * our peer process for the Internet domain. * * Parameters: "sock" is our socket * "host_name" * is filled in by this routine with the * corresponding ASCII names of our peer. * * if there doesn't exist a hostname in DNS etal, * the IP# will be inserted for the host_name * * "ipnum" is filled in with the ascii IP# */ void SOCKnetnames(sockfd, host_name, ipnum) int sockfd; char *host_name; char *ipnum; { struct sockaddr_in sa; int length; struct hostent *hp; length = sizeof(sa); if (getpeername(sockfd, (struct sockaddr *)&sa, &length)) /** May fail if sockfd has been closed **/ #ifdef VMS_SERVER { LOGGopher(-2, "getpeername() failure: %s", vms_errno_string()); if (ipnum != NULL) strcpy(ipnum,"Unknown"); if (host_name != NULL) strcpy(host_name,"Unknown"); return; } #else return; #endif if (ipnum != NULL) strcpy(ipnum, inet_ntoa(sa.sin_addr)); if (host_name != NULL) strcpy(host_name, inet_ntoa(sa.sin_addr)); #ifdef LOG_IP_ONLY hp = NULL; #else hp = gethostbyaddr((char *) &sa.sin_addr,sizeof (sa.sin_addr), AF_INET); #endif if (hp != NULL && host_name != NULL) (void) strcpy(host_name, hp->h_name); } #endif /** CMUIP etal **/ #if defined(VMS_SERVER) && defined(MULTINET) /* * This turns the keepalive option on * TGV recommended setting KEEPALIVE on the server, just in case * we somehow got a socket locked in a CLOSE_WAIT state (shouldn't * happen since we set REUSEADDR *before* we bind, but... it's * sure a lot better than rebooting your VAX/AXP node to get the * port back from a %MULTINET-F-EADDRINUSE error condition). */ void SOCKkeepalive(sockfd, onoff) int sockfd; boolean onoff; { #if defined(SO_KEEPALIVE) && !defined(NO_KEEPALIVE) int keepalive = onoff; if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof (keepalive)) < 0) LOGGopher(-1,"warning: can't turn %s keepalive sockopt, %s", onoff?"on":"off", vms_errno_string()); #endif } #endif .