This is sqUnixNetwork.c in view mode; [Download] [Up]
/* Unix socket support.
*
* Author: Ian Piumarta (ian.piumarta@inria.fr)
*
* Last edited: Mon Feb 2 17:02:20 1998 by piumarta (Ian Piumarta) on fricotin
*
* Notes:
*
* 1. UDP support is fully implemented here, but is missing in the
* image.
*
* 2. Sockets are completely asynchronous, but the resolver is still
* synchronous.
*
* 3. Due to bugs in Solaris and HP-UX (and possibly others) the SIGIO
* mechanism is no longer used. Instead aioPollForIO() is periodically
* called from HandleEvents in sqXWindow.c in order to check for I/O
* completion using select().
*
* $Log: sqUnixNetwork.c,v $
* Revision 1.2 1997/09/30 04:59:19 piumarta
* added asynchronous support
*
* Revision 1.1 1997/08/02 01:37:12 piumarta
* Initial revision
*/
static char rcsid[]=
"$Id: sqUnixNetwork.c,v 1.2 1997/09/30 05:00:23 piumarta Exp piumarta $";
#include "sq.h"
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#ifdef NEED_FILIO
# include <sys/filio.h>
#endif
#ifdef NEED_SELECT
# include <sys/select.h>
#endif
/* debugging stuff; can probably be deleted */
#ifdef DEBUG
# define FPRINTF(X) fprintf X
char *ticks= "-\\|/";
char *ticker= "";
# define DO_TICK() \
{ fprintf(stderr, "\r%c\r", *ticker); if (!*ticker++) ticker= ticks; }
#else
# define FPRINTF(X)
# define DO_TICK()
#endif
/*** Socket types ***/
#define TCPSocketType 0
#define UDPSocketType 1
/*** Resolver states ***/
#define ResolverUninitialized 0
#define ResolverSuccess 1
#define ResolverBusy 2
#define ResolverError 3
/*** TCP Socket states ***/
#define Unconnected 0x00
#define WaitingForConnection 0x01
#define Connected 0x02
#define OtherEndClosed 0x03
#define ThisEndClosed 0x04
static int thisNetSession= 0;
static int one= 1;
static char localHostName[MAXHOSTNAMELEN];
static u_long localHostAddress; /* WARNING: GROSS IPv4 ASSUMPTION!!! */
typedef struct privateSocketStruct {
int s; /* Unix socket */
int sema; /* io notification semaphore */
int sockState; /* connection + data state */
int sockError; /* errno after socket error */
struct sockaddr_in peer; /* default send/recv address for UDP */
} privateSocketStruct;
/*** Accessors for private socket members from a Squeak socket pointer ***/
#define PSP(S) ((privateSocketStruct *)((S)->privateSocketPtr))
#define SOCKET(S) (PSP(S)->s)
#define SOCKETSEMA(S) (PSP(S)->sema)
#define SOCKETSTATE(S) (PSP(S)->sockState)
#define SOCKETERROR(S) (PSP(S)->sockError)
#define SOCKETPEER(S) (PSP(S)->peer)
#define SIGNAL(PSS) signalSemaphoreWithIndex((PSS)->sema)
/*** Resolver state ***/
static char lastName[MAXHOSTNAMELEN+1];
static int lastAddr= 0;
static int lastError= 0;
static int resolverSema;
/*** asynchronous i/o support ***/
/* handler flags */
#define AIO_EX (0<<0)
#define AIO_RD (1<<0)
#define AIO_WR (1<<1)
#define AIO_RW (AIO_RD | AIO_WR)
typedef void (*AioHandler)(privateSocketStruct *pss, int errorFlag);
static privateSocketStruct *sockets[FD_SETSIZE];
static AioHandler handler[FD_SETSIZE];
static int lastSocket;
static fd_set readMask;
static fd_set writeMask;
static fd_set exceptionMask;
static void nullHandler(privateSocketStruct *, int);
static void acceptHandler(privateSocketStruct *, int);
static void connectHandler(privateSocketStruct *, int);
static void dataHandler(privateSocketStruct *, int);
static void closeHandler(privateSocketStruct *, int);
#ifdef DEBUG
static char *handlerName(AioHandler h) {
if (h == nullHandler) return "nullHandler";
if (h == acceptHandler) return "acceptHandler";
if (h == connectHandler) return "connectHandler";
if (h == dataHandler) return "dataHandler";
if (h == closeHandler) return "closeHandler";
return "***unknownHandler***";
}
#endif
void aioPollForIO(int microSeconds, int extraFd);
/* poll for io activity and call the appropriate handler(s) *
*
* Note: this can be called from ioProcessEvents with a zero timeout
* and from ioRelinquishProcessor with a non-zero timeout.
*
* "extraFd" is a file descriptor that is polled for reading but
* never handled -- this allows a relinquished CPU to return
* early if there is mouse or keyboard input activity. Essential
* for (e.g.) handling keyboard interrupts during i/o wait.
*/
void aioPollForIO(int microSeconds, int extraFd)
{
int fd;
fd_set rd, wr, ex;
struct timeval tv;
DO_TICK();
/* get out early if there is no pending i/o and no need to relinquish cpu */
if (!lastSocket && !microSeconds) return;
rd= readMask;
wr= writeMask;
ex= exceptionMask;
if (extraFd) FD_SET(extraFd, &rd);
tv.tv_sec= microSeconds / 1000000;
tv.tv_usec= microSeconds % 1000000;
{
int limit= (extraFd > lastSocket ? extraFd : lastSocket) + 1;
int result= select(limit, &rd, &wr, &ex, &tv);
if (result < 0)
perror("select");
if (result != 0)
for (fd= 0; fd < lastSocket + 1; ++fd) {
if (FD_ISSET(fd, &ex))
handler[fd](sockets[fd], 1);
else if (FD_ISSET(fd, &rd) || FD_ISSET(fd, &wr))
if (fd != extraFd)
handler[fd](sockets[fd], 0);
}
}
}
/* enable asynchronous notification for a socket */
static void aioEnable(privateSocketStruct *pss)
{
int fd= pss->s;
FPRINTF((stderr, "aioEnable(%d)\n", fd));
sockets[fd]= pss;
handler[fd]= nullHandler;
#ifdef FIOSNBIO /* HP-UKes again */
if (ioctl(fd, FIOSNBIO, (char *)&one) < 0) perror("ioctl(FIOSNBIO,1)");
#else
if (ioctl(fd, FIONBIO, (char *)&one) < 0) perror("ioctl(FIONBIO,1)");
#endif
}
/* install/change the handler for a socket */
static void aioHandle(privateSocketStruct *pss,
AioHandler handlerFn,
int flags)
{
int fd= pss->s;
FPRINTF((stderr, "aioHandle(%d,%s,%d)\n",
fd, handlerName(handlerFn), flags));
if (sockets[fd] != pss) {
fprintf(stderr, "aioHandle: bad match\n");
sockets[fd]= pss;
}
handler[fd]= handlerFn;
if (fd > lastSocket) lastSocket= fd;
if (flags & AIO_RD) FD_SET(fd, &readMask);
if (flags & AIO_WR) FD_SET(fd, &writeMask);
FD_SET(fd, &exceptionMask);
}
/* temporarily suspend asynchronous notification for a socket */
static void aioSuspend(privateSocketStruct *pss)
{
int fd= pss->s;
FPRINTF((stderr, "aioSuspend(%d)\n", fd));
if (fd) {
FD_CLR(fd, &readMask);
FD_CLR(fd, &writeMask);
/* FD_CLR(fd, &exceptionMask); */
handler[fd]= nullHandler;
/* keep lastSocket accurate (reduces to zero if no more sockets) */
while (lastSocket && !FD_ISSET(lastSocket, &exceptionMask))
--lastSocket;
}
}
/* definitively disable asynchronous notification for a socket */
static void aioDisable(privateSocketStruct *pss)
{
int fd= pss->s;
FPRINTF((stderr, "aioDisable(%d)\n", fd));
if (fd) {
FD_CLR(fd, &exceptionMask);
aioSuspend(pss);
sockets[fd]= 0;
handler[fd]= 0;
}
}
/* initialise asynchronous i/o handlers */
static void aioInit()
{
int i;
for (i= 0; i < FD_SETSIZE; i++) {
sockets[i]= 0;
handler[i]= 0;
}
FD_ZERO(&readMask);
FD_ZERO(&writeMask);
FD_ZERO(&exceptionMask);
lastSocket= 0;
signal(SIGPIPE, SIG_IGN);
}
/* disable handlers and close all sockets */
static void aioShutdown()
{
int i;
for (i= 0; i < lastSocket+1; i++)
if (sockets[i]) {
aioDisable(sockets[i]);
close(sockets[i]->s);
}
}
/*** Miscellaneous sundries ***/
/* answer the hostname for the given IP address */
static const char *addrToName(int netAddress)
{
u_long nAddr;
struct hostent *he;
lastError= 0; /* for the resolver */
nAddr= htonl(netAddress);
if ((he= gethostbyaddr((char *)&nAddr, sizeof(nAddr), AF_INET)))
return he->h_name;
lastError= h_errno; /* ditto */
return "";
}
/* answer the IP address for the given hostname */
static int nameToAddr(char *hostName)
{
struct hostent *he;
lastError= 0; /* ditto */
if ((he= gethostbyname(hostName)))
return ntohl(*(long *)(he->h_addr_list[0]));
lastError= h_errno; /* and one more ditto */
return 0;
}
/* answer whether the given socket is valid in this net session */
static int socketValid(SocketPtr s)
{
if ((s != 0) && (s->privateSocketPtr != 0) && (s->sessionID == thisNetSession))
return true;
success(false);
return false;
}
/* answer whether the socket can be read (or accept()ed) without blocking */
static int socketReadable(int s)
{
struct timeval tv= { 0, 0 };
fd_set fds;
FD_ZERO(&fds);
FD_SET(s, &fds);
return select(s+1, &fds, 0, 0, &tv) > 0;
}
/* answer whether the socket can be written without blocking */
static int socketWritable(int s)
{
struct timeval tv= { 0, 0 };
fd_set fds;
FD_ZERO(&fds);
FD_SET(s, &fds);
return select(s+1, 0, &fds, 0, &tv) > 0;
}
/*** aio handlers ***/
/* Each handler must at least:
* - suspend further handling for the associated socket
* - signal its semaphore to indicate that the operation is complete
*/
/* this handler should never normally be invoked */
static void nullHandler(privateSocketStruct *pss, int errFlag)
{
fprintf(stderr, "nullHandler(%d,%d)\n", pss->s, errFlag);
aioSuspend(pss);
if (errFlag)
pss->sockState= OtherEndClosed;
SIGNAL(pss); /* operation complete */
}
/* accept() can now be performed for the socket: call accept(),
and replace the server socket with the new client socket
leaving the client socket unhandled */
static void acceptHandler(privateSocketStruct *pss, int errFlag)
{
FPRINTF((stderr, "acceptHandler(%d,%d)\n", pss->s, errFlag));
aioSuspend(pss);
if (errFlag) {
/* error during listen() */
close(pss->s);
pss->sockState= Unconnected;
pss->sockError= EBADF; /* educated guess */
} else {
/* accept() is ready */
int newSock= accept(pss->s, 0, 0);
aioDisable(pss);
close(pss->s);
if (newSock < 0) {
/* error during accept() */
pss->sockError= errno;
pss->sockState= Unconnected;
perror("acceptHandler");
} else {
/* connection accepted */
pss->s= newSock;
pss->sockState= Connected;
aioEnable(pss);
}
}
SIGNAL(pss); /* operation complete */
}
/* connect() has completed: check errors, leaving the socket unhandled */
static void connectHandler(privateSocketStruct *pss, int errFlag)
{
FPRINTF((stderr, "connectHandler(%d,%d)\n", pss->s, errFlag));
aioSuspend(pss);
if (errFlag) {
/* error during asynchronous connect() */
close(pss->s);
pss->sockError= errno;
pss->sockState= Unconnected;
} else {
/* connect() has completed */
pss->sockState= Connected;
}
SIGNAL(pss); /* operation complete */
}
/* read or write data transfer is now possible for the socket */
static void dataHandler(privateSocketStruct *pss, int errFlag)
{
FPRINTF((stderr, "dataHandler(%d,%d)\n", pss->s, errFlag));
aioSuspend(pss);
if (errFlag)
/* error: almost certainly "connection closed by peer" */
pss->sockState= OtherEndClosed;
SIGNAL(pss); /* operation complete */
}
/* a non-blocking close() has completed -- finish tidying up */
static void closeHandler(privateSocketStruct *pss, int errFlag)
{
FPRINTF((stderr, "closeHandler(%d,%d)\n", pss->s, errFlag));
aioDisable(pss);
pss->sockState= Unconnected;
pss->s= 0;
SIGNAL(pss); /* operation complete */
}
/*** Squeak network functions ***/
/* start a new network session */
int sqNetworkInit(int resolverSemaIndex)
{
if (0 != thisNetSession) return 0; /* already initialised */
gethostname(localHostName, MAXHOSTNAMELEN);
localHostAddress= nameToAddr(localHostName);
thisNetSession= clock() + time(0);
if (0 == thisNetSession) thisNetSession= 1; /* 0 => uninitialised */
resolverSema= resolverSemaIndex;
aioInit();
return 0;
}
/* terminate the current network session (invalidates all open sockets) */
void sqNetworkShutdown(void)
{
thisNetSession= 0;
resolverSema= 0;
aioShutdown();
}
/*** Squeak Generic Socket Functions ***/
/* create a new socket */
void sqSocketCreateNetTypeSocketTypeRecvBytesSendBytesSemaID(
SocketPtr s, int netType, int socketType,
int recvBufSize, int sendBufSize, int semaIndex)
{
int newSocket= -1;
privateSocketStruct *pss;
s->sessionID= 0;
if (TCPSocketType == socketType)
/* --- TCP --- */
newSocket= socket(AF_INET, SOCK_STREAM, 0);
else if (UDPSocketType == socketType)
/* --- UDP --- */
newSocket= socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == newSocket) { /* socket() failed, or incorrect socketType */
success(false);
return;
}
setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
/* private socket structure */
pss= (privateSocketStruct *)calloc(1, sizeof(privateSocketStruct));
pss->s= newSocket;
pss->sema= semaIndex;
/* UDP sockets are born "connected" */
pss->sockState= (UDPSocketType == socketType) ? Connected : Unconnected;
pss->sockError= 0;
/* initial UDP peer := wildcard */
memset(&pss->peer, 0, sizeof(pss->peer));
pss->peer.sin_family= AF_INET;
pss->peer.sin_port= 0;
pss->peer.sin_addr.s_addr= INADDR_ANY;
/* Squeak socket */
s->sessionID= thisNetSession;
s->socketType= socketType;
s->privateSocketPtr= pss;
FPRINTF((stderr, "create(%d) -> %lx\n", SOCKET(s), PSP(s)));
/* Note: socket is in BLOCKING mode until aioEnable is called for it! */
}
/* return the state of a socket */
int sqSocketConnectionStatus(SocketPtr s)
{
if (!socketValid(s)) return -1;
return SOCKETSTATE(s);
}
/* TCP => start listening for incoming connections.
* UDP => associate the local port number with the socket.
*/
void sqSocketListenOnPort(SocketPtr s, int port)
{
struct sockaddr_in saddr;
if (!socketValid(s)) return;
FPRINTF((stderr, "listenOnPort(%d)\n", SOCKET(s)));
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family= AF_INET;
saddr.sin_port= htons((short)port);
saddr.sin_addr.s_addr= INADDR_ANY;
bind(SOCKET(s), (struct sockaddr*) &saddr, sizeof(saddr));
if (TCPSocketType == s->socketType) {
/* --- TCP --- */
/* set backlog to 1, since Squeak server sockets only ever accept a single
connection. (This is unforgivable, but that's the way things are.) */
listen(SOCKET(s), 1);
SOCKETSTATE(s)= WaitingForConnection;
aioEnable(PSP(s));
aioHandle(PSP(s), acceptHandler, AIO_RD); /* => accept() possible */
} else {
/* --- UDP --- */
}
}
/* TCP => open a connection.
* UDP => set remote address.
*/
void sqSocketConnectToPort(SocketPtr s, int addr, int port)
{
struct sockaddr_in saddr;
if (!socketValid(s)) return;
FPRINTF((stderr, "connectTo(%d)\n", SOCKET(s)));
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family= AF_INET;
saddr.sin_port= htons((short)port);
saddr.sin_addr.s_addr= htonl(addr);
if (UDPSocketType == s->socketType) {
/* --- UDP --- */
memcpy((void *)&SOCKETPEER(s), (void *)&saddr, sizeof(SOCKETPEER(s)));
SOCKETSTATE(s)= Connected;
} else {
/* --- TCP --- */
int result;
aioEnable(PSP(s));
result= connect(SOCKET(s), (struct sockaddr *)&saddr, sizeof(saddr));
FPRINTF((stderr, "connect() => %d\n", result));
if (result == 0) {
/* connection completed synchronously */
SOCKETSTATE(s)= Connected;
SIGNAL(PSP(s)); /* operation complete */
} else {
if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
/* asynchronous connection in progress */
SOCKETSTATE(s)= WaitingForConnection;
aioHandle(PSP(s), connectHandler, AIO_WR); /* => connect() done */
} else {
/* connection error */
perror("sqConnectToPort");
SOCKETSTATE(s)= Unconnected;
SOCKETERROR(s)= errno;
SIGNAL(PSP(s)); /* operation complete */
}
}
}
}
/* close the socket */
void sqSocketCloseConnection(SocketPtr s)
{
int result;
if (!socketValid(s)) return;
FPRINTF((stderr, "closeConnection(%d)\n", SOCKET(s)));
aioDisable(PSP(s));
SOCKETSTATE(s)= ThisEndClosed;
result= close(SOCKET(s));
if (result == -1 && errno != EWOULDBLOCK) {
/* error */
SOCKETSTATE(s)= Unconnected;
SOCKETERROR(s)= errno;
SIGNAL(PSP(s)); /* operation complete */
} else if (0 == result) {
/* close completed synchronously */
SOCKETSTATE(s)= Unconnected;
SOCKET(s)= 0;
SIGNAL(PSP(s)); /* operation complete */
} else {
/* asynchronous close in progress */
SOCKETSTATE(s)= ThisEndClosed;
aioHandle(PSP(s), closeHandler, AIO_EX); /* => close() done */
}
}
/* close the socket without lingering */
void sqSocketAbortConnection(SocketPtr s)
{
struct linger linger= { 0, 0 };
FPRINTF((stderr, "abortConnection(%d)\n", SOCKET(s)));
if (!socketValid(s)) return;
setsockopt(SOCKET(s), SOL_SOCKET, SO_LINGER,
(char *)&linger, sizeof(linger));
sqSocketCloseConnection(s);
}
/* Release the resources associated with this socket.
If a connection is open, abort it.*/
void sqSocketDestroy(SocketPtr s)
{
if (!socketValid(s)) return;
FPRINTF((stderr, "destroy(%d)\n", SOCKET(s)));
if (SOCKET(s)) sqSocketAbortConnection(s); /* close if necessary */
if (PSP(s)) free(PSP(s)); /* release private struct */
/* PSP(s)= 0;*/ /* HP-UKes cannot cope with this */
s->privateSocketPtr= 0;
}
/* answer the OS error code for the last socket operation */
int sqSocketError(SocketPtr s)
{
if (!socketValid(s)) return -1;
return SOCKETERROR(s);
}
/* return the local IP address bound to a socket */
int sqSocketLocalAddress(SocketPtr s)
{
struct sockaddr_in saddr;
int saddrSize= sizeof(saddr);
if (!socketValid(s)) return -1;
if (getsockname(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize) ||
AF_INET != saddr.sin_family)
return 0;
return ntohl(saddr.sin_addr.s_addr);
}
/* return the peer's IP address */
int sqSocketRemoteAddress(SocketPtr s)
{
struct sockaddr_in saddr;
int saddrSize;
if (!socketValid(s)) return -1;
if (TCPSocketType == s->socketType) {
/* --- TCP --- */
saddrSize= sizeof(saddr);
if (getpeername(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize) ||
AF_INET != saddr.sin_family)
return 0;
return ntohl(saddr.sin_addr.s_addr);
}
/* --- UDP --- */
return ntohl(SOCKETPEER(s).sin_addr.s_addr);
}
/* return the local port number of a socket */
int sqSocketLocalPort(SocketPtr s)
{
struct sockaddr_in saddr;
int saddrSize= sizeof(saddr);
if (!socketValid(s)) return -1;
if (getsockname(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize) ||
AF_INET != saddr.sin_family)
return 0;
return ntohs(saddr.sin_port);
}
/* return the peer's port number */
int sqSocketRemotePort(SocketPtr s)
{
struct sockaddr_in saddr;
int saddrSize;
if (!socketValid(s)) return -1;
if (TCPSocketType == s->socketType) {
/* --- TCP --- */
if (getpeername(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize) ||
AF_INET != saddr.sin_family)
return 0;
return ntohs(saddr.sin_port);
}
/* --- UDP --- */
return ntohs(SOCKETPEER(s).sin_port);
}
/* answer whether the socket has data available for reading */
int sqSocketReceiveDataAvailable(SocketPtr s)
{
if (!socketValid(s)) return -1;
if (SOCKETSTATE(s) == Connected) {
if (socketReadable(SOCKET(s))) return true;
aioHandle(PSP(s), dataHandler, AIO_RW);
}
return false;
}
/* answer whether the socket has space to receive more data */
int sqSocketSendDone(SocketPtr s)
{
if (!socketValid(s)) return -1;
if (SOCKETSTATE(s) == Connected) {
if (socketWritable(SOCKET(s))) return true;
aioHandle(PSP(s), dataHandler, AIO_RW);
}
return false;
}
/* read data from the socket s into buf for at most bufSize bytes.
answer the number actually read. For UDP, fill in the peer's address
with the approriate value. */
int sqSocketReceiveDataBufCount(SocketPtr s, int buf, int bufSize)
{
int nread;
if (!socketValid(s)) return -1;
if (UDPSocketType == s->socketType) {
/* --- UDP --- */
int addrSize= sizeof(SOCKETPEER(s));
if ((nread= recvfrom(SOCKET(s), (void*)buf, bufSize, 0,
(struct sockaddr *)&SOCKETPEER(s),
&addrSize)) <= 0) {
addrSize= sizeof(SOCKETPEER(s));
if (socketReadable(SOCKET(s)) &&
((nread= recvfrom(SOCKET(s), (void*)buf, bufSize, 0,
(struct sockaddr *)&SOCKETPEER(s),
&addrSize)) <= 0)) {
SOCKETERROR(s)= errno;
FPRINTF((stderr, "receiveData(%d) = %da\n", SOCKET(s), 0));
return 0;
}
}
} else {
/* --- TCP --- */
if ((nread= read(SOCKET(s), (void*)buf, bufSize)) <= 0) {
if (errno == EWOULDBLOCK) {
/* asynchronous read in progress */
aioHandle(PSP(s), dataHandler, AIO_RW); /* => retry */
return 0;
} else {
/* error: most probably "connection closed by peer" */
SOCKETSTATE(s)= OtherEndClosed;
SOCKETERROR(s)= errno;
FPRINTF((stderr, "receiveData(%d) = %db\n", SOCKET(s), 0));
return 0;
}
}
}
/* read completed synchronously */
FPRINTF((stderr, "receiveData(%d) = %d\n", SOCKET(s), nread));
aioSuspend(PSP(s));
return nread;
}
/* write data to the socket s from buf for at most bufSize bytes.
answer the number of bytes actually written. */
int sqSocketSendDataBufCount(SocketPtr s, int buf, int bufSize)
{
int nsent;
if (!socketValid(s)) return -1;
FPRINTF((stderr, "sendData(%d,%d)\n", SOCKET(s), bufSize));
if (UDPSocketType == s->socketType) {
/* --- UDP --- */
if ((nsent= sendto(SOCKET(s), (void *)buf, bufSize, 0,
(struct sockaddr *)&SOCKETPEER(s),
sizeof(SOCKETPEER(s)))) <= 0) {
if (socketWritable(SOCKET(s)) &&
((nsent= sendto(SOCKET(s), (void *)buf, bufSize, 0,
(struct sockaddr *)&SOCKETPEER(s),
sizeof(SOCKETPEER(s)))) <= 0)) {
SOCKETERROR(s)= errno;
return 0;
}
}
} else {
/* --- TCP --- */
if ((nsent= write(SOCKET(s), (char *)buf, bufSize)) <= 0) {
if (errno == EWOULDBLOCK) {
/* asynchronous write in progress */
aioHandle(PSP(s), dataHandler, AIO_RW); /* => data sent */
return 0;
} else {
/* error: most likely "connection closed by peer" */
SOCKETSTATE(s)= OtherEndClosed;
SOCKETERROR(s)= errno;
return 0;
}
}
}
/* write completed synchronously */
FPRINTF((stderr, "sendData(%d) = %d\n", SOCKET(s), nsent));
aioSuspend(PSP(s));
return nsent;
}
/*** Resolver functions ***/
/* Note: the Mac and Win32 implementations implement asynchronous lookups
* in the DNS. I can't think of an easy way to do this in Unix without
* going totally ott with threads or somesuch. If anyone knows differently,
* please tell me about it. - Ian
*/
/*** irrelevancies ***/
void sqResolverAbort(void) {}
void sqResolverStartAddrLookup(int address)
{
const char *res;
res= addrToName(address);
strncpy(lastName, res, MAXHOSTNAMELEN);
}
int sqResolverStatus(void)
{
if(!thisNetSession) return ResolverUninitialized;
if(lastError) return ResolverError;
return ResolverSuccess;
}
/*** trivialities ***/
int sqResolverAddrLookupResultSize(void) { return strlen(lastName); }
int sqResolverError(void) { return lastError; }
int sqResolverLocalAddress(void) { return nameToAddr(localHostName); }
int sqResolverNameLookupResult(void) { return lastAddr; }
void sqResolverAddrLookupResult(char *nameForAddress, int nameSize)
{
memcpy(nameForAddress, lastName, nameSize);
}
/*** name resolution ***/
void sqResolverStartNameLookup(char *hostName, int nameSize)
{
int len= (nameSize < MAXHOSTNAMELEN) ? nameSize : MAXHOSTNAMELEN;
memcpy(lastName, hostName, len);
lastName[len]= lastError= 0;
lastAddr= nameToAddr(lastName);
/* we're done before we even started */
signalSemaphoreWithIndex(resolverSema);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.