4.3BSD Sockets Interface

Overview

This is a standard low-level API for networking on Unix systems using 
Internet. It is not unheard of on DOS systems. At least three vendors, 
3Com, FTP Software and Novell,  offer DOS implementations. Although none 
are 100% compatible, they are all are at least close.

This hypertext serves two purposes. First, it is a quick reference to BSD 
sockets calls, for developers running Windows. Secondly, it emphasizes DOS 
implementations of BSD sockets, and their various quirks. In short, it is 
especially for DOS programmers who must interface with Unix networks.

The sockets interface is geared toward, but not limited to, a client-server 
architecture.

There are standard Unix networking services that build upon this layer. 
Examples:
	FTP (File Transfer Protocol)
	DNS (Domain Name System)

DOS Implementations referred to in this document
  FTP Software - PC/TCP ADK v2.05. FTP will soon have a newer product, 
  called WINSOCK, which will be more fully BSD comptatible. PC/TCP does not 
  support sockets on Windows.
  Novell - Lan Workplace for DOS Socket Library API. Comes with ExcelLan 
  (XLN)
  3Com - 3Com TCP with Demand Protocol

Author
  This document is meant to be useful, but not comprehensive. Send any 
  notice of errors or omissions to neil@gradient.com, or on Channel 1, to 
  Neil Rowland.

  This is version 1.0.1.

Fundamental object types:
  Socket - a logical connection between a client and a server, or, if 
  connectionless, a hub for communication from or to various other sockets 
  on other systems. A socket is identified by a socket descriptor, which is 
  an integer value.
  Local endpoint - IP address and port of this end of the connection.
  Remote endpoint - IP address and port of the far end of the connection.

Types of socket:
	SOCK_STREAM Stream - sequenced, two-way, guaranteed delivery, priority 
bypass.
	SOCK_DGRAM Datagram - much less sophisticated. Connectionless, not 
guaranteed delivery.
	SOCK_RAW Raw - very low level. Seems to underlie the others. Not of 
interest to application programmers.

Interface Types
  Synchronous blocking - API call returns to caller when operation is 
  completed.
  Synchronous non-blocking - if API call would take a while, returns 
  EWOULDBLOCK status.
  	A synchronous blocking socket can be made synchronous non-blocking 
  with the ioctl() call.
  Asynchronous - API call returns when operation is initiated, but not yet 
  completed.
  	Caller must provide a callback function, to be called when the 
  operation completes. Not available under 3Com, or in standard mode with 
  Novell.

Protocols and Protocol Families

PF_UNIX - UNIX internal
PF_INET - Internet family - the most popular
	ICMP - Internet Control Message Protocol
	TCP - Transmission Control Protocol - use with SOCK_STREAM sockets.
	UDP - User Datagram Protocol - use with SOCK_DGRAM sockets.
	IP - Internet Protocol - underlies TCP and UDP.
	ARP - Address Resolution Protocol
	RARP - Reverse Address Resolution Protocol
PF_IMPLINK-  imp link protocols
PF_PUP - pup protocols: e.g. BSP
PF_CHAOS	 - MIT CHAOS protocols
PF_OISCP	- OIS communication protocols
PF_NBS - NBS protocols
PF_ECMA	- European Computer Manufacturers
PF_DATAKIT - Datakit protocols
PF_CCITT - CCITT protocols, X.25 etc
The life of a socket

Establishing a connection:
	Client:
		Create the socket. - socket() function
		Bind it to the local endpoint - bind() function.
			Bind it to the remote endpoint (the server end) - connect() 
function.
	Server:
		Create the socket. - socket() function
		Bind it to the local endpoint - bind() function.
			Bind it to the remote endpoint (the client end) - listen() and 
accept() functions.

Using the connection:
	Client and server transfer messages back and forth with send and receive 
API calls.

Closing the connection:
  Either client or server decides to end the session. It informs the other, 
  by some application-specfic mechanism. Then client and server both call 
  soclose().

Using non-connected sockets
  If a socket is bound but not conected, then it can send to multiple 
  destinations, or receive from multiple sources. The calls sendto() and 
  recvfrom() come into play here.
API calls by category

Socket Maintenance
  accept()
  bind()
  close()/soclose/close_socket()
  connect()
  getpeername()
  getsockname()
  getsockopt()
  ioctl()
  listen()
  select()
  setsockopt()
  shutdown()
  socket()

Send and Receive
  sendto()
  recvfrom()
  write()/sowrite()
  send()
  read()/soread()
  recv()
  recvmsg()
  sendmsg()

Apparently Novell only
  readv()
  writev()

Asynchronous API calls
  Same as their synchronous counterparts, but append _anr to the name, and 
  result, post_routine and usercb to the parameter list. Not available 
  under 3Com, or in standard mode with Novell.

Internet Address Conversions
  unsigned long inet_addr (char*);				- converts ASCII string binding 
  to Internet address.
  unsigned long inet_lnaof (unsigned long);	- returns local network 
  address from Internet host address.
  unsigned long inet_makeaddr (unsigned long, unsigned long);
  										- constructs an Internet address, given network 
  number and local address.
  unsigned long inet_netof (unsigned long);	- returns network number from 
  Internet host address.
  unsigned long  inet_network (char*);			- converts ASCII string 
  binding to Internet network number.
  char*  inet_ntoa (unsigned long, char*);	- converts Internet address to 
  ASCII string binding.

Byte Order Conversions (not available in ULTRIX)
	unsigned short htons (unsigned short);	converts short from host to 
network order.
	unsigned short ntohs (unsigned short);	converts short from network to 
host order.
	unsigned long  ntohl (unsigned long);	converts long from network to host 
order.
	unsigned long  htonl (unsigned long);	converts long from host to network 
order.

NOTE: Under Unix System V, the stream oriented file calls (close, read, 
write, unlink) do double duty, also working with stream sockets. Novell's 
offering, XLN, uses "so" prefixes on stream-oriented calls to distinguish 
them from the DOS file equivalents. FTP Software is more like unix, and 
provides alternative versions of these calls with the same names, but you 
have to include a special header.
Error and status codes

Most API routines return 0 for success or -1 for error. All routines stash 
a result code in the global variable errno. It will be one of these 
values...

EOK = 0 - no error.

EALREADY - operation already in progress.
EBADF - bad socket number. Socket may not be initialized properly.
EINTR - interrupted by user.
EINVAL - invalid argument.
EWOULDBLOCK - socket is synchronous nonblocking and would block. Retry.
EINPROGRESS - socket is busy. Retry.
EPROTOTYPE - wrong protocol.
ENOPROTOOPT - protocol not available.
EPROTONOSUPPORT - protocol not supported.
EOPNOTSUPP - operation not supported
EPFNOSUPPORT - protocol family not supported.
EADDRINUSE - connection already exists on the same socket pair.
ECONNABORTED - connection was aborted.
ECONNRESET - other end has aborted. Close the socket.
ENOBUFS - out of buffer space. Socket not created.
ENOTSOCK - descriptor is not a socket.
EISCONN - already connected. Ignore if synchronous nonblocking.
ENOTCONN - not yet connected. Wait.
ESHUTDOWN - socket has been closed.
ETIMEDOUT - network timed out. Close the socket.
ECONNREFUSED - unable to connect - other side refused.
ENOTINSTLD - protocol not installed.
EASYNCNOTSUPP - asynchronous operation cannot be performed.
ESYNCNOTSUPP - synchronous function cannot be pefformed here.
ENO_RCB - socket library ran out of resources (overloaded).

All these values are >=0.
Socket Option Flags

#define	SO_DEBUG	0x0001		/* turn on debugging info recording */
#define	SO_ACCEPTCONN	0x0002		/* socket has had listen() */
#define	SO_REUSEADDR	0x0004		/* allow local address reuse */
#define	SO_KEEPALIVE	0x0008		/* keep connections alive */
#define	SO_DONTROUTE	0x0010		/* just use interface addresses */
#define	SO_BROADCAST	0x0020		/* permit sending of broadcast msgs */
#define	SO_USELOOPBACK	0x0040		/* bypass hardware when possible */
#define	SO_LINGER	0x0080		/* linger on close if data present */
#define	SO_OOBINLINE	0x0100		/* leave received OOB data in line */
#define SO_SMALL	0x0200	
#define	SO_LARGE	0x0400
#define	SO_DONTLINGER	0x0800		/* This used to be ~SO_LINGER */

The structure passed as optval with SO_LINGER:

struct	linger {
	int	l_onoff;		/* option on/off */
	int	l_linger;		/* linger time */
};
Data Structures

fd_set

/* Message header for recvmsg and sendmsg calls. */
struct msghdr {
	CHARPTR	msg_name;		/* optional address */
	int	      msg_namelen;	/* size of address */
	IOVEC_PTR msg_iov;		/* scatter/gather array */
	int	      msg_iovlen;	/* # elements in msg_iov */
	CHARPTR	msg_accrights;	/* access rights sent/received */
	int	msg_accrightslen;
};

struct sockproto {
	short	sp_family;		/* protocol family */
	short	sp_protocol;		/* protocol within family */
};

struct sockaddr {
	short	sa_family;		/* address family */
	char	sa_data[14];		/* up to 14 bytes of direct address */
};

struct sockaddr_link {
	short		sl_family;
	short		sl_types[6];
	short		sl_zero;
};

struct timeval {
	long	tv_sec;		/* seconds */
	long	tv_usec;	/* and microseconds */
};

struct iovec {
    char*     iov_base;
    unsigned int iov_len;
};

These typedefs are not available on all systems:

typedef  struct sockaddr    *SADDR_PTR;    /* socket address pointer */
typedef  struct msghdr      *MSG_PTR;
typedef  struct iovec       *IOVEC_PTR;
typedef  struct timeval     *TIMEVAL_PTR;
typedef  struct fd_set      *FDSET_PTR;
fd_set

FD_SETSIZE is a #define, indicating the max number of sockets. On XLN, it 
is 128. On FTP Software, it is 20.

/*
 * Select uses bit masks of file descriptors in longs.
 * These macros manipulate such bit fields (the filesystem macros use 
chars).
 * FD_SETSIZE may be defined by the user, but the default here
 * should be >= NOFILE (param.h).
 */

typedef long	fd_mask;

#define NFDBITS	(long)(sizeof(fd_mask) * NBBY)	/* bits per mask */
#ifndef howmany
#define	howmany(x, y)	(((x)+((y)-1))/(y))
#endif
typedef	struct fd_set {
	fd_mask	fds_bits[howmany(FD_SETSIZE, NFDBITS)];
} fd_set;

The following macros are provided as a convenient way to work with the bit 
fields:
	FD_ZERO(&fdset)
	FD_SET(fd, &fdset)
	FD_CLR(fd, &fdset)
	FD_ISSET(fd, &fdset)

Asynchronous API calls

Same as their synchronous counterparts, but append _anr to the name, and 
result, post_routine and usercb to the parameter list. Not available under 
3Com, or in standard mode with Novell.

result points to an integer to receive a result code, which is either a 
socket descriptor, or, if negative, a negative error code.

post_routine points to a callback routine, which is called when the 
operation completes. It must have the form:
	int post_routine(unsigned long usercb, int errorcode)

usercb is a number, provided by the application, to uniquely identify the 
request. The app passes this to the API call, which then passes it to the 
post_routine.

errorcode, passed to post_routine is the status code of the operation.

int  accept_anr(int s, struct sockaddr*, int *, int  *, int (far *)(), 
unsigned long);
int  soclose_anr(int, int  *, int (far *)(), unsigned long);
int  connect_anr(int,struct sockaddr*,int,int  *, int (far *)(), unsigned 
long);
int  soread_anr(int,char  *,int, int  *, int (far *)(), unsigned long);
int  readv_anr(int,struct iovec*,int,int  *, int (far *)(), unsigned long);
int  recv_anr(int,char *,int,int, int  *, int (far *)(), unsigned long);
int  recvfrom_anr(int,char  *,int,int,struct sockaddr*,int  *,int  *,  int 
(far *)(), unsigned long);
int  recvmsg_anr(int,MSG_PTR,int, int *, int (far *)(), unsigned long);
int  select_anr(int,struct fd_set* readfds, struct fd_set* writefds, struct 
fd_set* exceptfds, TIMEVAL_PTR, int  *, int (far *)(), unsigned long );
int  send_anr(int,char *,int,int,int  *, int (far *)(), unsigned long);
int  sendmsg_anr(int,MSG_PTR,int, int  *, int (far *)(), unsigned long );
int  sendto_anr(int,char  *,int,int,struct sockaddr*,int, int  *, int (far 
*)(), unsigned long );
int  sowrite_anr(int,char  *,int, int  *, int (far *)(), unsigned long );
int  writev_anr(int,struct iovec*,int, int *, int (far *)(), unsigned long 
);

int  accept(int s, struct sockaddr* addr, int* addrlen);

Accept a connection on a socket. Used by servers. First must do bind() and 
listen() on the socket.

s is the socket descriptor, as returned by socket().
addr will hold the name of the other end of the connection.
addrlen is the size of the buffer addr on entry, and the size of the name 
on exit.

int  bind(int s, struct sockaddr* name, int namelen);

Assigns a name (sockaddr) to an unnamed socket.

s is the socket descriptor, as returned by socket().
name is the sockaddr for this end of the connection.
namelen is its length.
int  close/soclose/close_socket(int s);

Close a socket. Under Novell, the name is soclose. Under 3Com, the name is 
close_socket.

s is the socket descriptor, as returned by socket().

Returns 0 iff successful.
int  connect(int s, struct sockaddr* name, int namelen);

Initiate a connection on a socket. Used by clients.

s is the socket descriptor, as returned by socket(). Must be bound by bind().
name is the sockaddr for the far end of the connection.
namelen is its length.
int  getpeername(int s, struct sockaddr* name, int* namelen);

Get name of connected peer.

s is the socket descriptor, as returned by socket().
name receives the sockaddr for the peer.
namelen receives its length.

Returns 0 iff successful, -1 otherwise.
int  getsockname(int s, struct sockaddr* name, int* len);

Get name of this socket.

s is the socket descriptor, as returned by socket().
name receives the sockaddr.
namelen receives its length.

Returns 0 iff successful, -1 otherwise.
int  getsockopt(int s, int level, int optname, char* optval, int* optlen);

Query a socket's option settings.

s is the socket descriptor, as returned by socket().
level is SOL_SOCKET for socket level options, else protocol number of 
appropriate protocol.
optname is a socket option identifier.
optval is a buffer for the option values.
optlen points to where the length of optval gets stashed.
int  ioctl(int s, int request, char* argp);

Various socket settings.

s is the socket descriptor, as returned by socket().
request takes one of these values:
	FIONREAD - get number of bytes to read.
	FIONBIO - set/clear non-blocking I/O
	SIOCATMARK - query at OOB mark. Not supported by 3Com.
argp - where number of bytes to read gets stashed, iff FIONREAD, or the 
flag value otherwise.
int  listen(int s, int backlog);

Checks for connections on a socket. Used by servers.
Applies only to connections of type SOCK_STREAM.

s is the socket descriptor, as returned by socket().
backlog is the max number of entries in the backlog (input) queue.

Returns 0 iff successful, -1 iff error.
int  read/soread(int s, char* buf, int nbytes);

Read in (receive) data from a stream socket. Under Novell, the name is 
soread.
Applies only to connections of type SOCK_STREAM.

s is the socket descriptor, as returned by socket().
buf is the data buffer.
nbytes is the length of the data buffer.
int  readv(int s, struct iovec* iov, int iovcnt);

Read (receive) into a chain of buffers. Novell only (?)
Applies only to connected sockets.

s is the socket descriptor, as returned by socket().
iov is an array of iovecs.
iovcnt is the number of iovecs in iov.
int  recv(int s, char* buf, int len, int flags);

Receive into a buffer.
Applies only to connected sockets.

s is the socket descriptor, as returned by socket().
buf is the data buffer.
len is the length of the data buffer.
flags can contain:
	MSG_PEEK  -peek at incoming buffer
	MSG_OOB - process out-of-band data. Not supported by 3Com.

Returns the length of the message, or -1 iff error.
int  recvfrom(int s, char* buf, int len, int flags, struct sockaddr* from, 
int* fromlen);

Receive data and know whence it came..

s is the socket descriptor, as returned by socket().
buf is the data buffer.
len is the length of the data buffer.
flags can contain:
	MSG_PEEK  -peek at incoming buffer
	MSG_OOB - process out-of-band data. Not supported by 3Com.
from  is where the source socket name gets stashed. NULL iff don't want it.
	WARNING: Under FTP Software, from doesn't work for socket streams.
fromlen is the length of from.

Returns the length of the message, or -1 iff error.
int  recvmsg(int s, MSG_PTR msg, int flags);

Receive a message. Not available with FTP Software or 3COM.

s is the socket descriptor, as returned by socket().
flags can contain:
	MSG_OOB - process out-of-band data.

Returns the length of the message, or -1 iff error.
int  select(int nfds, struct fd_set* readfds, struct fd_set* writefds, 
struct fd_set* exceptfds, TIMEVAL_PTR timeout);

Multiplexing - examines sets of I/O descriptors to see if ready for a given 
operation. Each passed struct fd_set* points to a bit array (fd_set), one 
bit per possible socket descriptor value. The values of the bit define a 
set of possible selectors.

On entry, the bit fields specify the selectors to check. On exit, the bit 
fields indicate the subset which met the criterion.

nfds 		- number of descriptor values (starting from 0) to check. No 
greater than FD_SETSIZE.
readfds 	- available for reading. NULL iff not interested.
writefds 	- available for writing. NULL iff not interested.
exceptfds 	- have an exception pending. NULL iff not interested.
timeout 	- points to timeout interval. If NULL, then there is no timeout.

NOTE: If timeout is NULL, select() will block indefinitely until at least 
one socket in a bit field meets the corresponding criterion.

NOTE: On both FTP Software and Novell, this call is always non-blocking. If 
it can't finish quickly, it will return EWOULDBLOCK in errno, in which case 
you should retry.
int  send(int s, char* msg, int len, int flags);

Send data. Applies only to connected sockets.

s is the socket descriptor, as returned by socket().
buf is the data buffer.
len is the length of the data.
flags can contain:
	MSG_DONTROUTE
	MSG_OOB - process out-of-band data. Not supported by 3Com.

Returns the length of the message sent, or -1 iff error.
int  sendmsg(int s, MSG_PTR msg, int flags);

Transmit a message. Not available with FTP Software or 3COM.

s is the socket descriptor, as returned by socket().
flags can contain:
	MSG_OOB - process out-of-band data.

Returns the length of the message sent, or -1 iff error.
int  sendto(int s, char * msg, int len, int flags, struct sockaddr* to, int 
len);

Send via a socket to an arbitrary destination socket.

s is the socket descriptor, as returned by socket().
buf is the data buffer.
len is the length of the data.
flags can contain:
	MSG_DONTROUTE -  Not supported by 3Com.
	MSG_OOB - process out-of-band data. Not supported by 3Com.
to  is the destination socket
tolen is the length of to.

Returns the length of the message sent, or -1 iff error.

WARNING: under FTP Software, with connected stream socket, maps to send (to 
field ignored).
int  setsockopt(int s, int level, int optname, char* optval, int optlen);

Set an option for a socket.

s is the socket descriptor, as returned by socket().
level is SOL_SOCKET for socket level options, else protocol number of 
appropriate protocol.
optname is a socket option identifier.
optval is a buffer with the option values.
optlen points to the length of optval.
int  shutdown(int s, int how);

Shutdown sleected operations for a socket. Applies only to connected 
sockets.

s is the socket descriptor, as returned by socket().
how specfies which operations will be disabled.
	0 - disable receives
	1 - disable sends
	2 - disable both
Returns 0 iff successful, -1 iff error.
int  socket(int domain, int type, int protocol);

Creates a socket (not yet bound or connected), and allocates a descriptor 
for it.

domain - protocol family, e.g. PF_INET.
type - socket type, e.g. SOCK_DGRAM.
protocol should be zero.

Retuns a socket descriptor, or -1 iff error.
int  write/sowrite(int s, char* buf, int nbytes);

Write out data to a stream socket. Under Novell, the name is sowrite.
Applies only to connected sockets.

s is the socket descriptor, as returned by socket().
buf is the data buffer.
nbytes is the length of the data.
int  writev(int s, struct iovec* iov, int iovcnt);

Write (send) from a chain of buffers. Novell only(?)
Applies only to connected sockets.

s is the socket descriptor, as returned by socket().
iov is an array of iovecs.
iovcnt is the number of iovecs in iov.

