[net.sources] Library rtns for servers/clients using sockets in 4.2bsd

srradia@watmath.UUCP (sanjay Radia) (01/30/85)

The following routines provide a  higher level interface to the
4.2 ipc routines  for use with servers and cliants in the 
UNIX and INET domains. The man page has a number of examples which
should really go into the ipc-primer manual.

---------------------------Cut Here---------------------------------
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# server.3 ipc_getiservaddr.c ipc_getuservaddr.c ipc_initservsocket.c

echo x - server.3
cat > "server.3" << '//E*O*F server.3//'
.TH SERVER 3 "25 July 1984"
.UC 4
.SH NAME
ipc_initservsocket, ipc_getiservaddr, ipc_getuservaddr  - server/client ipc routines.
.SH SYNOPSIS
.nf
.PP
.ft B
#include <sys/types.h>
#include <sys/socket.h>
sock = ipc_initservsocket(servicename, addressformat, socketype, protocol)
int sock, addressformat, socketype;
char *servicename, *protocol;
.PP
.ft B
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
ipc_getiservaddr(athost, servicename, protocol, addr)
char *athost, *servicename, *protocol;
struct sockaddr_in *addr;
.PP
.ft B
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
ipc_getuservaddr(socketpath, addr, addrlen)
char *socketpath;
struct sockaddr_un *addr;
int *addrlen;
.fi
.SH DESCRIPTION
.BR Ipc_initservsocket
is used by a server (daemon) to initialize a socket from which to receive
requests from clients. 
The
.I addressformat
parameter specifies an address format with which addresses specified
in later operations using the socket should be interpreted.
The currently understood formats are
.PP
.RS
.nf
.ta 1.25i 1.75i
AF_UNIX	(UNIX path names),
AF_INET	(ARPA Internet addresses),
.fi
.RE
.PP
The socket has the indicated
.I socketype
which specifies the semantics of communication.  Currently
defined types are:
.PP
.RS
.nf
SOCK_STREAM
SOCK_DGRAM
.fi
.RE
.PP
For
.B AF_INET
domain the
.I protocol
together with the
.I servicename
uniquely identifies the service port to be used.
The service port is obtained from searching for a matching entry in
.I /etc/services).
.I Protocol
specifies a particular protocol the server wishes to use.
This protocol is specified in
.I /etc/services
for each of the services.
If
.I protocol
is NULL then the first matching entry is used.
.PP
For the
.B AF_UNIX
domain, the
.I servicename
is used as the pathname for the socket in the file system.
The pathaname is unlinked before binding.
It is the resposibility of the caller to ensure that no other server
is listing on that socket pathname
.I (servicename).
The
.I protocol
parameter is not used for this domain.
.PP
.BR Ipc_getiservaddr
is used to get the address of a server on host
.I athost
for the
.I AF_INET
domain. If 
.I athost
is NULL then the local machine is used.
This address can later be used to form a connection
.I (connect(2))
or when using 
.B sento
or
.B sendmsg
.I (send(2)).
.PP
.PP
.BR Ipc_getuservaddr
is used to get the address of a server for the
.I AF_UNIX
domain. This address can later be used to form a connection
.I (connect(2))
or when using 
.B sento
or
.B sendmsg
.I (send(2)).
.SH ERRORS
In case of error, a negative number is returned:
.PP
.RS
.nf
.ta 1.25i 1.75i
-1	unsupported addressformat.
-2	could not create the socket, see perror() and errno.
-3	service not found (AF_INET domain)
-4	could not bind, see perro() and errno.
-5	bad host name.
.fi
.RE
.PP
In addition, an external variable
.I ipc_error
points to a string containing an error message.
.SH EXAMPLES
Example of client and server using
.B udp
sockets in the
.B AF_INET
domain is:
.nf
client()
{	char reply[80];
	struct sockaddr_in serv_addr;
	struct sockaddr from; int fromlen = sizeof(from);
	int s;
	extern char *ipc_error;

	if (ipc_getiservaddr(NULL, "SERVER", "udp", &serv_addr) != 0)
		error(ipc_error);
	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) <0)
		error("client:socket : %d\n", s);
	if (sendto(s, "the request", 12, 0, &serv_addr, sizeof(serv_addr)) <0 )
		error("client: request failed");
	/*
	 * To be safe, the socket should be made non-blocking.
	 * The client should timeout and retransmit the request.
	 * See recv(2) for details.
	 */
	if( recvfrom(s, reply, sizeof(reply), 0, &from, &fromlen)<0)
		error("client: recvfrom failed");
	.
	.
}

server()
{	char buf[80];
	int s;
	struct sockaddr from; int fromlen = sizeof(from);
	extern char *ipc_error;

	if((s = ipc_initservsocket("SERVER", AF_INET, SOCK_DGRAM, "udp")) <0) 
		error("server: %s\n", ipc_error);

	while (recvfrom(s, buf, sizeof(buf), 0, &from, &fromlen)>=0) {
		.
		.
		if (sendto(s, "the reply", 10, 0, &from, fromlen) <0 )
			printf("server: reply failed");
		.
		.
	}
}

.fi
.PP
Example of client and server using
.B tcp
sockets in the
.B AF_UNIX
domain is:
.nf
extern char *ipc_error;
client()
{	char buf[80];
	struct sockaddr_un from; int fromlen = sizeof(from);
	struct sockaddr_un serv_addr;
	int serv_addrlen,s;

	if (ipc_getuservaddr("SERVER", &serv_addr, &serv_addrlen) != 0)
		error(ipc_error);
	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) <0)
		error("client:socket : %d\n", s);
	if (connect(s,  &serv_addr, serv_addrlen) <0 )
		error("sender:connect");
	while (write(s, "nullRequest\n", 6) == 6 )
		;
	error("client: write failed");
}

server()
{	struct sockaddr_un from; int fromlen = sizeof(from);
	int s,g;

	if ((s = ipc_initservsocket("tcp_socket", AF_UNIX, SOCK_STREAM, NULL))<0) 
		error("server: %s\n", ipc_error);
	listen(s, 5);
	for (;;) {
		if ((g = accept(s, &from, &fromlen)) <0)
			printf("receiver:accept failed");
		else
			serve_request(s); /* server request from socket s */
	}
}
serve_request(s)
{
	char buf[80];
	if ((i = read(s, &buf[0], sizeof(buf)-1)) <= 0)
		printf("serve_request: read error %d\n", i);
	/* can process request in buf */
	.
	.
}
.fi
.SH SEE ALSO
accept(2), bind(2), connect(2), getsockname(2), getsockopt(2),
ioctl(2), listen(2), recv(2),
select(2), send(2), shutdown(2), socket(2), socketpair(2)
.br
``A 4.2BSD Interprocess Communication Primer''.
//E*O*F server.3//

echo x - ipc_getiservaddr.c
cat > "ipc_getiservaddr.c" << '//E*O*F ipc_getiservaddr.c//'
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

/*
 *    SEE server(3l) for details.
 */

#define E_UNSUPPORTED	-1
#define E_SOCKET	-2
#define E_SERVICE	-3
#define E_BIND		-4
#define E_HOST		-5

#define NULL		0

char *ipc_error;


ipc_getiservaddr(athost, servicename, protocol, addr)
char *athost;
char *servicename;
char *protocol;
struct sockaddr_in *addr;
{
	struct servent *sp;
	struct hostent *hp;

	char host[30];
	if (!athost) {
		(void) gethostname(&host[0], sizeof(host));
		athost = &host[0];
	}

	if ((sp = getservbyname(servicename, protocol))
					== (struct servent *) NULL) {
		ipc_error = "ipc_getiservaddr(): unknow service";
		return(E_SERVICE);
	}

	if ((hp=gethostbyname(athost)) == (struct hostent *) NULL) {
		ipc_error = "ipc_getservaddr(): unknown host";
		return(E_HOST);
	}

	bzero((char *) addr, sizeof(*addr));
	bcopy(hp->h_addr, (char *)&addr->sin_addr, hp->h_length);
	addr->sin_family = hp->h_addrtype;
	addr->sin_port = sp->s_port;
	return(0);
}
//E*O*F ipc_getiservaddr.c//

echo x - ipc_getuservaddr.c
cat > "ipc_getuservaddr.c" << '//E*O*F ipc_getuservaddr.c//'

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>

/*
 *    SEE server(3l) for details.
 */

#define E_UNSUPPORTED	-1
#define E_SOCKET	-2
#define E_SERVICE	-3
#define E_BIND		-4
#define E_HOST		-5



ipc_getuservaddr(socketpath, addr, addrlen)
char *socketpath;
struct sockaddr_un *addr;
int *addrlen;
{
	addr->sun_family = AF_UNIX;
	strcpy(addr->sun_path, socketpath);
	*addrlen = strlen(socketpath) + sizeof(addr->sun_family);
	return(0);
}
//E*O*F ipc_getuservaddr.c//

echo x - ipc_initservsocket.c
cat > "ipc_initservsocket.c" << '//E*O*F ipc_initservsocket.c//'
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <netdb.h>


#define E_UNSUPPORTED	-1
#define E_SOCKET	-2
#define E_SERVICE	-3
#define E_BIND		-4

#define NULL		0

char *ipc_error;

ipc_initservsocket(servicename, addressformat, socketype, protocol)
char *servicename;
char *protocol;
int addressformat, socketype;
{
	register s;
	extern int errno;
	extern char *strcpy();

	errno = 0;

	if ((s = socket(addressformat, socketype, 0)) < 0) {
		ipc_error = "ipc_initservsocket(): cannot get socket";
		return(E_SOCKET);
	}
	switch (addressformat) {
	case AF_INET:
	    {
		struct servent *sp;
		struct sockaddr_in sin;

		if ((sp = getservbyname(servicename, protocol))
						== (struct servent *) NULL) {
			ipc_error = "ipc_initservsocket(): unknow service";
			return(E_SERVICE);
		}
		sin.sin_family = AF_INET;
		sin.sin_port = sp->s_port;
		sin.sin_addr.s_addr = INADDR_ANY; /*if multi nets connected to my host */
		if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
			ipc_error = "ipc_initservsocket(): INET bind failed";
			return(E_BIND);
		}
		return(s);
	    }

	case AF_UNIX:
	    {
		struct sockaddr_un sun;

		/* servicename is pathname for the socket */
		(void) unlink(servicename);

		sun.sun_family = AF_UNIX;
		(void) strcpy(sun.sun_path, servicename);
		if (bind(s, (struct sockaddr *)&sun, strlen(sun.sun_path) + 2) < 0) {
			ipc_error = "ipc_initservsocket(): UNIX bind failed";
			return(E_BIND);
		}
		return(s);
	    }

	default:
		ipc_error = "ipc_initservsocket(): bad paramters";
		return(E_UNSUPPORTED);
	}
}
//E*O*F ipc_initservsocket.c//

exit 0
-- 

		sanjay
		UUCP:	...!{ utzoo,decvax,ihnp4,allegra}!watmath!srradia
		ARPA:	srradia%watmath%waterloo.csnet@csnet-relay.arpa
		CSNET:	srradia%watmath@waterloo.CSNET