This repository has been archived on 2024-01-04. You can view files and clone it, but cannot push or open issues or pull requests.
ncsa-mosaic/libdtm/socket.c

777 lines
19 KiB
C

/*****************************************************************************
*
* NCSA DTM version 2.3
* May 1, 1992
*
* NCSA DTM Version 2.3 source code and documentation are in the public
* domain. Specifically, we give to the public domain all rights for future
* licensing of the source code, all resale rights, and all publishing rights.
*
* We ask, but do not require, that the following message be included in all
* derived works:
*
* Portions developed at the National Center for Supercomputing Applications at
* the University of Illinois at Urbana-Champaign.
*
* THE UNIVERSITY OF ILLINOIS GIVES NO WARRANTY, EXPRESSED OR IMPLIED, FOR THE
* SOFTWARE AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT LIMITATION,
* WARRANTY OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE
*
*****************************************************************************/
/*********************************************************************
**
** $Header: /X11/mosaic/cvsroot/xmosaic3/libdtm/socket.c,v 1.2 1995/10/13 06:33:27 spowers Exp $
**
**********************************************************************/
/*
* $Log: socket.c,v $
* Revision 1.2 1995/10/13 06:33:27 spowers
* Solaris support added.
*
* Revision 1.1.1.1 1995/01/11 00:03:03 alanb
* New CVS source tree, Mosaic 2.5 beta 4
*
* Revision 2.5 1994/12/29 23:40:27 alanb
* I'm committing with a new symbolic revision number.
*
* Revision 1.1.1.1 1994/12/28 21:37:34 alanb
*
* Revision 1.3 1993/10/29 03:46:50 marca
* Tweaks.
*
* Revision 1.2 1993/10/06 06:19:10 ebina
* Ditto const shit
*
* Revision 1.1.1.1 1993/07/04 00:03:14 marca
* Mosaic for X version 2 distribution
*
* Revision 1.1 1993/01/18 21:50:44 marca
* I think I got it now.
*
* Revision 1.28 92/04/30 20:25:27 jplevyak
* Changed Version to 2.3.
*
* Revision 1.27 1992/04/06 15:58:49 jplevyak
* Fixed minor problems for machines little Endian machines.
*
* Revision 1.26 92/03/10 22:07:10 jplevyak
* Added changed for PC/MAC from Quincey Koziol (koziol@ncsa.uiuc.edu)
* with modification.
*
* Revision 1.25 1992/02/28 03:40:24 jplevyak
* int/long confict fix (no diff on workstations)
* ,
*
* Revision 1.24 92/01/30 19:33:07 jplevyak
* Fix bug in MAC version of dtm_get_ipaddr.
*
* Revision 1.23 1992/01/15 17:05:37 creiman
* Added typecast to dtm_socket_init:getsockname
*
* Revision 1.22 1992/01/14 16:31:40 creiman
* Removed mac #include
*
* Revision 1.21 1991/12/17 23:46:30 jefft
* dtm_socket_init used to only determine the socketname for logical ports,
* it now correctly sets the sockaddr_in structure maintained within the DTM
* port structure for every call.
*
* Revision 1.20 1991/10/29 22:07:10 sreedhar
* struct sockaddr * type casting
*
* Revision 1.19 1991/10/29 16:38:58 jplevyak
* Fixed bug in code that parses addressses. (extra (STDINT)).
*
* Revision 1.18 1991/10/16 23:26:00 jplevyak
* Fixed debugging message.
*
* Revision 1.17 1991/10/15 18:21:25 jplevyak
* Changed memcpy to structure cast, select field and assign. This
* is less kludgy and not only that, it works on the CRAY which the other
* did not.
*
* Revision 1.16 1991/10/14 16:49:32 jplevyak
* Fix problem with physical addressing.
*
* Revision 1.15 1991/10/10 15:15:04 jplevyak
* Fixed naming convensions.
*
* Revision 1.14 91/09/26 20:21:55 jplevyak
* Cosmetics
*
* Revision 1.13 91/09/18 15:33:08 jplevyak
* Added additional parameter to dtm_socket_init
*
* Revision 1.12 91/09/13 20:28:52 sreedhar
* accept :9900 change
*
* Revision 1.11 1991/09/13 20:13:35 sreedhar
* take current host as default
*
* Revision 1.10 1991/08/19 18:53:37 jefft
* Fixed bug with dtm_socket_init, now checks port number for absolute
* address instead of the IP address (which isn't used anyway).
*
* Revision 1.9 1991/08/15 18:56:35 sreedhar
* Changes for logical portname version
*
* Revision 1.7 1991/06/11 15:19:45 sreedhar
* disclaimer added
*
* Revision 1.6 1991/06/07 16:07:21 sreedhar
* Changes for sequence start message
*
* Revision 1.5 1991/05/30 15:52:10 sreedhar
* Changes for readMsg/writeMsg internal release
*
* Revision 1.4 1990/12/11 14:11:38 jefft
* made dtm_get_ipaddr CRAY specific to fix final portability problem.
*
* Revision 1.3 90/11/21 12:43:15 jefft
* Fixed portibility problem with dtm_get_ipaddr.
*
* Revision 1.2 90/11/21 10:54:18 jefft
* Added new routine, dtm_get_ipaddr. It returns an ascii string of the
* current hosts IP address.
*
* Revision 1.1 90/11/08 16:39:40 jefft
* Initial revision
*
*/
/*
+++++ System call - merge dtm_connect, dtm_quick_connect +++++
Check on whether dtm_get_ipaddr and dtm_init_sockaddr can be merged.
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#ifdef _ARCH_MSDOS
#include <io.h>
#include <time.h>
#include <stdlib.h>
#include <nmpcip.h>
#include "uio.h"
#else
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/socket.h>
#endif
#include <string.h>
/* Machine specific header file(s) */
#ifdef SOLARIS
#include <sys/filio.h>
#endif
#ifdef RS6000
#include <sys/select.h>
#endif
#ifdef _ARCH_MSDOS
#include <nmpcip.h>
#else
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#include <errno.h>
#include "dtmint.h"
#include "debug.h"
static int buf_size = DTM_BUFF_SIZE;
/*
dtm_parse_ipaddr()
Check whetherer given address string is in dotted
decimal notation and if so, to return the address in network byte
order.
Return values : TRUE, if in dotted decimal notation.
( network order address returned thru *addr )
FALSE, if not.
*/
#ifdef DTM_PROTOTYPES
int dtm_parse_ipaddr(char *s,unsigned long *addr )
#else
int dtm_parse_ipaddr( s, addr )
char *s; /* address string */
unsigned long *addr; /* location to return network byte order address */
#endif
{
int b1, b2, b3, b4;
int got;
if( (got = sscanf(s, "%d.%d.%d.%d", &b1, &b2, &b3, &b4)) != 4 ) {
DTMerrno = DTMADDR;
return DTMERROR;
}
*addr = htonl(b1 << 24 | b2 << 16 | b3 << 8 | b4);
return DTM_OK;
}
/*
dtm_quick_select()
Check whether a socket (s) has count bytes ready.
*/
#ifdef DTM_PROTOTYPES
int dtm_quick_select(int s,int *count)
#else
int dtm_quick_select(s, count)
int s;
int *count;
#endif
{
fd_set filedes;
static struct timeval timeout = {0L, 0L};
DBGFLOW("# dtm_quick_select called.\n");
FD_ZERO(&filedes);
FD_SET(s, &filedes);
#ifdef __hpux
if (select(32, (int *)&filedes, (int *)NULL, (int *)NULL, &timeout)) {
#else
if (select(32, &filedes, (fd_set *)NULL, (fd_set *)NULL, &timeout)) {
#endif
ioctl(s, FIONREAD, count);
return TRUE;
} else {
*count = 0;
return FALSE;
}
}
/*
dtm_select()
Wait (time) seconds for count bytes to be ready on socket s.
*/
#ifdef DTM_PROTOTYPES
int dtm_select(int s,int32 *count,int32 time )
#else
int dtm_select( s, count, time )
int s;
int32 *count;
int32 time;
#endif
{
fd_set filedes;
static struct timeval timeout = { 0L, 0L };
DBGFLOW("# dtm_select called.\n");
timeout.tv_sec = time ;
FD_ZERO( &filedes );
FD_SET( s, &filedes );
#ifdef __hpux
if( (*count = select( 32, (int *)&filedes, (int *)NULL, (int *)NULL,
#else
if( (*count = select( 32, &filedes, (fd_set *)NULL, (fd_set *)NULL,
#endif
&timeout ) )) {
ioctl( s, FIONREAD, count );
return TRUE;
} else {
return FALSE;
}
}
/*
dtm_accept().
Function to accept connection request on specified socket.
*/
#ifdef DTM_PROTOTYPES
int dtm_accept(int s,S_ADDR *sn,struct timeval *timeout )
#else
int dtm_accept( s, sn, timeout )
int s;
S_ADDR *sn;
struct timeval *timeout ;
#endif
{
int snsize = sizeof (S_ADDR);
DBGFLOW( "dtm_accept called.\n");
DBGMSG1( "dtm_accept: sockfd = %d\n", s );
/*
Await connect for specified time period only.
if timeout == NULL, it means just goahead and accept,
else wait for specified period and accept only if
connection request arrives in that period.
*/
if ( timeout ) {
fd_set readmask ;
fd_set *fchk = &readmask ;
int nf ;
FD_ZERO( fchk );
FD_SET( s, fchk );
#ifdef __hpux
nf = select( FD_SETSIZE, (int *)fchk, (int *)0, (int *)0, timeout );
#else
nf = select( FD_SETSIZE, fchk, (fd_set *)0, (fd_set *)0, timeout );
#endif
if ( nf < 0 ) {
DBGINT( "dtm_accept: select errno %d\n", errno );
DTMerrno = DTMSELECT ;
return DTMERROR ;
}
if ( nf == 0 ) {
/* No connect request in specified time */
DBGFLOW( "dtm_accept: timed out\n" );
return DTMERROR ;
}
}
/* accept connections on socket */
if ((s = accept(s, (struct sockaddr *)sn, &snsize)) < 0 ) {
DTMerrno = DTMSOCK;
DBGINT("dtm_accept: error %d accepting connection.", errno );
return DTMERROR ;
}
return s;
}
/*
dtm_connect()
Attempt to connect to the the address sn, returning
the connected port in *s.
returns DTMERROR on failure. DTM_OK on success.
*/
#ifdef DTM_PROTOTYPES
int dtm_connect(S_ADDR *sn,int *s)
#else
int dtm_connect(sn, s)
S_ADDR *sn;
int *s;
#endif
{
int d;
int refusedcount = 0;
DBGFLOW("dtm_connect called.\n");
DBGINT( "dtm_connect: s_addr = %x\n",
ntohl( sn -> sin_addr.s_addr ) );
DBGINT( "dtm_connect: sin_port = %d\n",
ntohs( sn -> sin_port ));
while (TRUE) {
/* create socket */
if ((d = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
DTMerrno = DTMSOCK;
DTMERR("dtm_connect: could not create socket.");
return DTMERROR;
}
/* attempt to connect to receiver */
if (connect(d, (struct sockaddr *)sn, sizeof (S_ADDR)) < 0) {
/* if connection refused, try again in 2 second */
if (errno == ECONNREFUSED) {
close(d);
sleep(2);
if ((refusedcount += 1) > DTM_REFUSE_LIMIT) {
DTMerrno = DTMTIMEOUT;
return DTMERROR;
} else
continue;
} else {
/* system error, can not connect, quit */
DTMerrno = DTMSOCK;
DTMERR("dtm_connect: could not connect.");
return DTMERROR;
}
} else {
/* connect complete, set working socket to original socket */
*s = d;
setsockopt(*s, IPPROTO_TCP, TCP_NODELAY, (char *)&d, sizeof d);
setsockopt(*s, SOL_SOCKET, SO_SNDBUF, (char *)&buf_size,
sizeof(int));
return DTM_OK;
}
} /* end while */
}
/*
dtm_quick_connect()
*/
#ifdef DTM_PROTOTYPES
int dtm_quick_connect(S_ADDR *sn,int *s)
#else
int dtm_quick_connect(sn, s)
S_ADDR *sn;
int *s;
#endif
{
int d;
DBGFLOW("# dtm_quick_connect called.\n");
/* create socket */
if ((d = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
DTMerrno = DTMSOCK;
DBGFLOW("dtm_quick_connect: could not create socket.");
return DTMERROR;
}
/* attempt to connect to receiver */
if (connect(d, (struct sockaddr *)sn, sizeof (S_ADDR)) < 0) {
/* if connection refused */
if (errno == ECONNREFUSED) {
close(d);
DTMerrno = DTMTIMEOUT;
return DTMERROR;
} else {
/* system error, can not connect, quit */
DTMerrno = DTMSOCK;
DBGFLOW("dtm_quick_connect: could not connect.");
return DTMERROR;
}
} else {
/* else connection has been made */
*s = d;
setsockopt(*s, IPPROTO_TCP, TCP_NODELAY, (char *)&d, sizeof d);
setsockopt(*s, SOL_SOCKET, SO_SNDBUF, (char *)&buf_size, sizeof (int));
return DTM_OK;
}
}
#ifdef DTM_PROTOTYPES
int dtm_end_connect(int s)
#else
int dtm_end_connect(s)
int s;
#endif
{
struct linger lbuf ;
DBGFLOW("# dtm_end_connect called.\n");
DBGINT( "dtm_end_connect: sockfd %d\n", s );
#if 0
lbuf.l_onoff = 0 ;
setsockopt( s, SOL_SOCKET, SO_LINGER, &lbuf, sizeof( struct linger ) );
#endif
return close( s );
}
/*
Return values :
On success,
Direct - host address in network byte order.
Indirect - *ipaddr has host address in dotted
decimal notation.
On error, 0.
Notes:
Error is returned as 0, since an internet address
of 0 is not possible for any host ( 0 refers to 'this' host
in internet context ).
*/
#ifdef DTM_PROTOTYPES
unsigned long dtm_get_ipaddr(char *ipaddrstr )
#else
unsigned long dtm_get_ipaddr( ipaddrstr )
char *ipaddrstr ;
#endif
{
char hostname[MAXHOSTNAMELEN];
struct hostent *hp;
unsigned long tmp;
DBGFLOW( "dtm_get_ipaddr called\n" );
/* get hostname */
gethostname( hostname, sizeof hostname );
#ifdef _ARCH_MACOS
/* check if hostname is in dotted decimal notation - this is a Mac-Hack */
if ( dtm_parse_ipaddr( hostname, &tmp ) != DTMERROR ) {
strcpy( ipaddrstr , hostname );
return tmp;
}
#endif
/* lookup IP address */
if( (hp = gethostbyname(hostname)) == NULL ) {
DTMerrno = DTMHOST;
return 0;
}
/* extract dotted decimal address */
{
struct in_addr inaddr ;
#ifdef _ARCH_MSDOS
inaddr = *((struct in_addr *)( hp -> h_addr)) ;
strcpy( ipaddrstr , inet_ntoa( inaddr.s_addr ));
#else
inaddr = *((struct in_addr *)( hp -> h_addr_list[ 0 ])) ;
strcpy( ipaddrstr , inet_ntoa( inaddr ));
#endif
}
DBGINT( "dtm_get_ipaddr: dotted decimal address = '%s'\n", ipaddrstr );
return inet_addr( ipaddrstr ) ;
}
/*
Function to acquire and bind a UDP or TCP port.
*/
#ifdef DTM_PROTOTYPES
int dtm_socket_init(S_ADDR *sockaddr,int porttype,int fLogicalName )
#else
int dtm_socket_init( sockaddr, porttype, fLogicalName )
S_ADDR *sockaddr;
int porttype;
int fLogicalName;
#endif
{
int sockfd;
int type;
int protocol;
int opt = 1;
int sockaddrsize = sizeof (struct sockaddr_in);
char buf[128];
DBGMSG1( "dtm_socket_init: sockaddr -> s_addr = %x\n",
ntohl( sockaddr -> sin_addr.s_addr) );
DBGMSG1( "dtm_socket_init: sockaddr -> sin_port = %d\n",
ntohs( sockaddr -> sin_port) );
sockaddr -> sin_family = AF_INET ;
if ( fLogicalName ) {
/*
Logical name had been supplied for makeport.
Assign port from system ( sin_port = 0 ), and accept
from all network interfaces for multi-homed host
( INADDR_ANY ).
*/
sockaddr -> sin_addr.s_addr = htonl( INADDR_ANY );
sockaddr -> sin_port = htons( 0 ) ;
}
/* Acquire appropriate socket ( UDP or TCP ) */
if( porttype == INPORTTYPE ) {
sockaddr -> sin_addr.s_addr = htonl( INADDR_ANY );
type = SOCK_STREAM ;
protocol = IPPROTO_TCP ;
} else {
type = SOCK_DGRAM ;
protocol = IPPROTO_UDP ;
}
if( (sockfd = socket( sockaddr -> sin_family, type, protocol )) < 0 ) {
DTMerrno = DTMSOCK ;
DBGINT( "dtm_socket_init: socket create error %d", errno );
return DTMERROR ;
}
/* Set socket options. */
setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof opt );
setsockopt( sockfd, SOL_SOCKET, SO_RCVBUF, (char *)&buf_size, sizeof(int) );
if( porttype == INPORTTYPE ) {
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sizeof opt );
}
/* Bind name to socket */
DBGFLOW( "dtm_socket_init: Before bind\n" );
DBGINT( "dtm_socket_init: sockfd = %d\n", sockfd );
DBGINT( "dtm_socket_init: sockaddr -> family = %d\n",
sockaddr -> sin_family );
DBGINT( "dtm_socket_init: sockaddr -> s_addr = %x\n",
ntohl( sockaddr -> sin_addr.s_addr) );
DBGINT( "dtm_socket_init: sockaddr -> sin_port = %d\n",
ntohs( sockaddr -> sin_port) );
if( bind( sockfd, (struct sockaddr *)sockaddr,
sizeof( struct sockaddr_in ) ) < 0 ) {
DTMerrno = DTMSOCK ;
DBGMSG1( "dtm_socket_init: could not bind to sockaddr, errno = %d\n",
errno );
return DTMERROR;
}
/* Listen at socket for TCP port, buffer for 5 pending connections */
if( porttype == INPORTTYPE )
listen( sockfd, 5 );
/*
Get the actual assigned (port) address ( netid/hostid/portid )
- netid/hostid from dtm_get_ipaddr(),portid from getsockname().
Netid/hostid and portid is in network byte order.
Assumption - host is not multi-homed.
*/
/* get the port number */
if(getsockname(sockfd,(struct sockaddr *)sockaddr,&sockaddrsize)<0) {
DBGINT( "dtm_socket_init: Unable to get sin_port, errno %d\n", errno );
DTMerrno = DTMSOCK ;
return DTMERROR;
}
/* get the IP address */
if( (sockaddr -> sin_addr.s_addr = dtm_get_ipaddr( buf )) == 0) {
DBGFLOW( "dtm_socket_init: Unable to get s_addr\n" );
DTMerrno = DTMSOCK ;
return DTMERROR ;
}
DBGFLOW( "dtm_socket_init: Verify nethostid/portid\n" );
DBGINT( "dtm_socket_init: Nethostid = %x\n",
ntohl( sockaddr -> sin_addr.s_addr ) );
DBGINT( "dtm_socket_init: Portid = %d \n",
ntohs( sockaddr -> sin_port ) );
DBGINT( "dtm_socket_init: exit sockfd = %d\n", sockfd );
return sockfd ;
}
/*
Function to get sockaddr if portname is specified in
physical portname format ( e.g. "kankakee:9900" )
Return value : 0 on success,
DTMERROR on error
Notes : Algorithm -
1. Check portname format.
2. If logical format, sockaddr.sin_addr.s_addr = 0
3. If physical format, fill in sockaddr.sin_port and
sockaddr.sin_addr.s_addr.
It returns:
sockaddr in network byte order.
*pfLogicalName = TRUE if the port is logical.
*/
#ifdef DTM_PROTOTYPES
int dtm_init_sockaddr(struct sockaddr_in *sockaddr,char *portname,
int *pfLogicalName )
#else
int dtm_init_sockaddr( sockaddr, portname, pfLogicalName )
struct sockaddr_in *sockaddr ;
char *portname ; /* read-only */
int *pfLogicalName;
#endif
{
char *host ;
char *port ;
char lportname[ PNAMELEN ] ;
char hostname[ MAXHOSTNAMELEN ] ;
u_long saddr_temp;
strncpy( lportname, portname, PNAMELEN - 1 );
lportname[ PNAMELEN - 1 ] = '\0' ;
DBGFLOW( "dtm_init_sockaddr called\n" );
if( lportname[0] == ':' ) {
host = NULL ;
port = lportname + 1;
} else {
if( (port = strchr( lportname, ':' )) == NULL ) {
/* Logical format */
DBGSTR( "dtm_init_sockaddr: logical portname %s\n", lportname );
sockaddr -> sin_port = htons( 0 );
sockaddr -> sin_addr.s_addr = htonl(0);
*pfLogicalName = TRUE;
DBGINT( "dtm_init_sockaddr: sin_port = %d\n",
ntohs( sockaddr->sin_port ));
return DTM_OK;
}
*port++ = '\0';
host = lportname;
}
*pfLogicalName = FALSE;
/*
Physical format - hostname is either in dotted decimal
notation ( call ipaddr() ) or direct or missing.
*/
if( host == NULL ) {
gethostname( hostname, sizeof hostname );
host = hostname ;
}
DBGINT( "dtm_init_sockaddr: host %s\n", host );
DBGINT( "dtm_init_sockaddr: port %s\n", port );
if( dtm_parse_ipaddr( host, &saddr_temp ) == DTMERROR) {
struct hostent *hp ;
if( (hp = gethostbyname( host )) == NULL ) {
DBGFLOW("dtm_init_sockaddr: gethostbyname returns error\n");
DTMerrno = DTMHOST ;
return DTMERROR ;
} else {
#ifdef _ARCH_MSDOS
saddr_temp = ((struct in_addr *)(hp->h_addr))->s_addr;
#else
saddr_temp = ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
#endif
}
}
sockaddr->sin_addr.s_addr = saddr_temp;
/* Fill in port id */
sockaddr -> sin_port = htons((unsigned short)atol( port ));
DBGINT( "dtm_init_sockaddr: nethostid = %x\n",
ntohl( sockaddr -> sin_addr.s_addr ));
DBGINT( "dtm_init_sockaddr: portid = %d\n", ntohs( sockaddr -> sin_port) );
return DTM_OK ;
}