forked from Imagelibrary/rtems
2000-09-28 Joel Sherrill <joel@OARcorp.com>
* rtems_webserver/Makefile.am, rtems_webserver/base64.c, rtems_webserver/base64.c: Renamed base64.c to wbase64.c. * rtems_webserver/sock.c: Added file missed in merger.
This commit is contained in:
789
c/src/libnetworking/rtems_webserver/sock.c
Normal file
789
c/src/libnetworking/rtems_webserver/sock.c
Normal file
@@ -0,0 +1,789 @@
|
||||
/*
|
||||
* sock.c -- Posix Socket upper layer support module for general posix use
|
||||
*
|
||||
* Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/******************************** Description *********************************/
|
||||
|
||||
/*
|
||||
* Posix Socket Module. This supports blocking and non-blocking buffered
|
||||
* socket I/O.
|
||||
*/
|
||||
|
||||
/********************************** Includes **********************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if UEMF
|
||||
#include "uemf.h"
|
||||
#else
|
||||
#include <socket.h>
|
||||
#include <types.h>
|
||||
#include <unistd.h>
|
||||
#include "emfInternal.h"
|
||||
#endif
|
||||
|
||||
/************************************ Locals **********************************/
|
||||
|
||||
socket_t **socketList; /* List of open sockets */
|
||||
int socketMax; /* Maximum size of socket */
|
||||
int socketHighestFd = -1; /* Highest socket fd opened */
|
||||
|
||||
/***************************** Forward Declarations ***************************/
|
||||
|
||||
static int socketDoOutput(socket_t *sp, char *buf, int toWrite, int *errCode);
|
||||
static int tryAlternateSendTo(int sock, char *buf, int toWrite, int i,
|
||||
struct sockaddr *server);
|
||||
|
||||
/*********************************** Code *************************************/
|
||||
/*
|
||||
* Write to a socket. Absorb as much data as the socket can buffer. Block if
|
||||
* the socket is in blocking mode. Returns -1 on error, otherwise the number
|
||||
* of bytes written.
|
||||
*/
|
||||
|
||||
int socketWrite(int sid, char *buf, int bufsize)
|
||||
{
|
||||
socket_t *sp;
|
||||
ringq_t *rq;
|
||||
int len, bytesWritten, room;
|
||||
|
||||
a_assert(buf);
|
||||
a_assert(bufsize >= 0);
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop adding as much data to the output ringq as we can absorb. Initiate a
|
||||
* flush when the ringq is too full and continue. Block in socketFlush if the
|
||||
* socket is in blocking mode.
|
||||
*/
|
||||
rq = &sp->outBuf;
|
||||
for (bytesWritten = 0; bufsize > 0; ) {
|
||||
if ((room = ringqPutBlkMax(rq)) == 0) {
|
||||
if (socketFlush(sid) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if ((room = ringqPutBlkMax(rq)) == 0) {
|
||||
if (sp->flags & SOCKET_BLOCK) {
|
||||
#if WIN || CE
|
||||
int errCode;
|
||||
if (! socketWaitForEvent(sp, FD_WRITE | SOCKET_WRITABLE,
|
||||
&errCode)) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
len = min(room, bufsize);
|
||||
ringqPutBlk(rq, (unsigned char *) buf, len);
|
||||
bytesWritten += len;
|
||||
bufsize -= len;
|
||||
buf += len;
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Write a string to a socket
|
||||
*/
|
||||
|
||||
int socketWriteString(int sid, char_t *buf)
|
||||
{
|
||||
#if UNICODE
|
||||
char *byteBuf;
|
||||
int r, len;
|
||||
|
||||
len = gstrlen(buf);
|
||||
byteBuf = ballocUniToAsc(buf, len);
|
||||
r = socketWrite(sid, byteBuf, len);
|
||||
bfreeSafe(B_L, byteBuf);
|
||||
return r;
|
||||
#else
|
||||
return socketWrite(sid, buf, strlen(buf));
|
||||
#endif /* UNICODE */
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Read from a socket. Return the number of bytes read if successful. This
|
||||
* may be less than the requested "bufsize" and may be zero. Return -1 for
|
||||
* errors. Return 0 for EOF. Otherwise return the number of bytes read.
|
||||
* If this routine returns zero it indicates an EOF condition.
|
||||
* which can be verified with socketEof()
|
||||
|
||||
* Note: this ignores the line buffer, so a previous socketGets
|
||||
* which read a partial line may cause a subsequent socketRead to miss some
|
||||
* data. This routine may block if the socket is in blocking mode.
|
||||
*
|
||||
*/
|
||||
|
||||
int socketRead(int sid, char *buf, int bufsize)
|
||||
{
|
||||
socket_t *sp;
|
||||
ringq_t *rq;
|
||||
int len, room, errCode, bytesRead;
|
||||
|
||||
a_assert(buf);
|
||||
a_assert(bufsize > 0);
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sp->flags & SOCKET_EOF) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rq = &sp->inBuf;
|
||||
for (bytesRead = 0; bufsize > 0; ) {
|
||||
len = min(ringqLen(rq), bufsize);
|
||||
if (len <= 0) {
|
||||
/*
|
||||
* if blocking mode and already have data, exit now or it may block
|
||||
* forever.
|
||||
*/
|
||||
if ((sp->flags & SOCKET_BLOCK) &&
|
||||
(bytesRead > 0)) {
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* This flush is critical for readers of datagram packets. If the
|
||||
* buffer is not big enough to read the whole datagram in one hit,
|
||||
* the recvfrom call will fail.
|
||||
*/
|
||||
ringqFlush(rq);
|
||||
room = ringqPutBlkMax(rq);
|
||||
len = socketGetInput(sid, (char *) rq->endp, room, &errCode);
|
||||
if (len < 0) {
|
||||
if (errCode == EWOULDBLOCK) {
|
||||
if ((sp->flags & SOCKET_BLOCK) &&
|
||||
(bytesRead == 0)) {
|
||||
continue;
|
||||
}
|
||||
if (bytesRead >= 0) {
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
} else if (len == 0) {
|
||||
/*
|
||||
* If bytesRead is 0, this is EOF since socketRead should never
|
||||
* be called unless there is data yet to be read. Set the flag.
|
||||
* Then pass back the number of bytes read.
|
||||
*/
|
||||
if (bytesRead == 0) {
|
||||
sp->flags |= SOCKET_EOF;
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
ringqPutBlkAdj(rq, len);
|
||||
len = min(len, bufsize);
|
||||
}
|
||||
memcpy(&buf[bytesRead], rq->servp, len);
|
||||
ringqGetBlkAdj(rq, len);
|
||||
bufsize -= len;
|
||||
bytesRead += len;
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Get a string from a socket. This returns data in *buf in a malloced string
|
||||
* after trimming the '\n'. If there is zero bytes returned, *buf will be set
|
||||
* to NULL. If doing non-blocking I/O, it returns -1 for error, EOF or when
|
||||
* no complete line yet read. If doing blocking I/O, it will block until an
|
||||
* entire line is read. If a partial line is read socketInputBuffered or
|
||||
* socketEof can be used to distinguish between EOF and partial line still
|
||||
* buffered. This routine eats and ignores carriage returns.
|
||||
*/
|
||||
|
||||
int socketGets(int sid, char_t **buf)
|
||||
{
|
||||
socket_t *sp;
|
||||
ringq_t *lq;
|
||||
char c;
|
||||
int rc, len;
|
||||
|
||||
a_assert(buf);
|
||||
*buf = NULL;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
lq = &sp->lineBuf;
|
||||
|
||||
while (1) {
|
||||
|
||||
if ((rc = socketRead(sid, &c, 1)) < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
/*
|
||||
* If there is a partial line and we are at EOF, pretend we saw a '\n'
|
||||
*/
|
||||
if (ringqLen(lq) > 0 && (sp->flags & SOCKET_EOF)) {
|
||||
c = '\n';
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If a newline is seen, return the data excluding the new line to the
|
||||
* caller. If carriage return is seen, just eat it.
|
||||
*/
|
||||
if (c == '\n') {
|
||||
len = ringqLen(lq);
|
||||
if (len > 0) {
|
||||
*buf = ballocAscToUni(lq->servp, len);
|
||||
} else {
|
||||
*buf = NULL;
|
||||
}
|
||||
ringqFlush(lq);
|
||||
return len;
|
||||
|
||||
} else if (c == '\r') {
|
||||
continue;
|
||||
}
|
||||
ringqPutcA(lq, c);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Flush the socket. Block if the socket is in blocking mode.
|
||||
* This will return -1 on errors and 0 if successful.
|
||||
*/
|
||||
|
||||
int socketFlush(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
ringq_t *rq;
|
||||
int len, bytesWritten, errCode;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
rq = &sp->outBuf;
|
||||
|
||||
/*
|
||||
* Set the background flushing flag which socketEventProc will check to
|
||||
* continue the flush.
|
||||
*/
|
||||
if (! (sp->flags & SOCKET_BLOCK)) {
|
||||
sp->flags |= SOCKET_FLUSHING;
|
||||
}
|
||||
|
||||
/*
|
||||
* Break from loop if not blocking after initiating output. If we are blocking
|
||||
* we wait for a write event.
|
||||
*/
|
||||
while (ringqLen(rq) > 0) {
|
||||
len = ringqGetBlkMax(&sp->outBuf);
|
||||
bytesWritten = socketDoOutput(sp, (char*) rq->servp, len, &errCode);
|
||||
if (bytesWritten < 0) {
|
||||
if (errCode == EINTR) {
|
||||
continue;
|
||||
} else if (errCode == EWOULDBLOCK || errCode == EAGAIN) {
|
||||
#if WIN || CE
|
||||
if (sp->flags & SOCKET_BLOCK) {
|
||||
int errCode;
|
||||
if (! socketWaitForEvent(sp, FD_WRITE | SOCKET_WRITABLE,
|
||||
&errCode)) {
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Ensure we get a FD_WRITE message when the socket can absorb
|
||||
* more data (non-blocking only.) Store the user's mask if we
|
||||
* haven't done it already.
|
||||
*/
|
||||
if (sp->saveMask < 0 ) {
|
||||
sp->saveMask = sp->handlerMask;
|
||||
socketRegisterInterest(sp,
|
||||
sp->handlerMask | SOCKET_WRITABLE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
ringqGetBlkAdj(rq, bytesWritten);
|
||||
}
|
||||
/*
|
||||
* If the buffer is empty, reset the ringq pointers to point to the start
|
||||
* of the buffer. This is essential to ensure that datagrams get written
|
||||
* in one single I/O operation.
|
||||
*/
|
||||
if (ringqLen(rq) == 0) {
|
||||
ringqFlush(rq);
|
||||
}
|
||||
/*
|
||||
* Restore the users mask if it was saved by the non-blocking code above.
|
||||
* Note: saveMask = -1 if empty. socketRegisterInterest will set handlerMask
|
||||
*/
|
||||
if (sp->saveMask >= 0) {
|
||||
socketRegisterInterest(sp, sp->saveMask);
|
||||
sp->saveMask = -1;
|
||||
}
|
||||
sp->flags &= ~SOCKET_FLUSHING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Return the count of input characters buffered. We look at both the line
|
||||
* buffer and the input (raw) buffer. Return -1 on error or EOF.
|
||||
*/
|
||||
|
||||
int socketInputBuffered(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (socketEof(sid)) {
|
||||
return -1;
|
||||
}
|
||||
return ringqLen(&sp->lineBuf) + ringqLen(&sp->inBuf);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Return true if EOF
|
||||
*/
|
||||
|
||||
int socketEof(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return sp->flags & SOCKET_EOF;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Return the number of bytes the socket can absorb without blocking
|
||||
*/
|
||||
|
||||
int socketCanWrite(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return sp->outBuf.buflen - ringqLen(&sp->outBuf) - 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Add one to allow the user to write exactly SOCKET_BUFSIZ
|
||||
*/
|
||||
|
||||
void socketSetBufferSize(int sid, int in, int line, int out)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (in >= 0) {
|
||||
ringqClose(&sp->inBuf);
|
||||
in++;
|
||||
ringqOpen(&sp->inBuf, in, in);
|
||||
}
|
||||
|
||||
if (line >= 0) {
|
||||
ringqClose(&sp->lineBuf);
|
||||
line++;
|
||||
ringqOpen(&sp->lineBuf, line, line);
|
||||
}
|
||||
|
||||
if (out >= 0) {
|
||||
ringqClose(&sp->outBuf);
|
||||
out++;
|
||||
ringqOpen(&sp->outBuf, out, out);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Create a user handler for this socket. The handler called whenever there
|
||||
* is an event of interest as defined by handlerMask (SOCKET_READABLE, ...)
|
||||
*/
|
||||
|
||||
void socketCreateHandler(int sid, int handlerMask, socketHandler_t handler,
|
||||
int data)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return;
|
||||
}
|
||||
sp->handler = handler;
|
||||
sp->handler_data = data;
|
||||
socketRegisterInterest(sp, handlerMask);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Delete a handler
|
||||
*/
|
||||
|
||||
void socketDeleteHandler(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return;
|
||||
}
|
||||
sp->handler = NULL;
|
||||
socketRegisterInterest(sp, 0);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Socket output procedure. Return -1 on errors otherwise return the number
|
||||
* of bytes written.
|
||||
*/
|
||||
|
||||
static int socketDoOutput(socket_t *sp, char *buf, int toWrite, int *errCode)
|
||||
{
|
||||
struct sockaddr_in server;
|
||||
int bytes;
|
||||
|
||||
a_assert(sp);
|
||||
a_assert(buf);
|
||||
a_assert(toWrite > 0);
|
||||
a_assert(errCode);
|
||||
|
||||
*errCode = 0;
|
||||
|
||||
#if WIN || CE
|
||||
if ((sp->flags & SOCKET_ASYNC)
|
||||
&& ! socketWaitForEvent(sp, FD_CONNECT, errCode)) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Write the data
|
||||
*/
|
||||
if (sp->flags & SOCKET_BROADCAST) {
|
||||
server.sin_family = AF_INET;
|
||||
#if UEMF || LITTLEFOOT
|
||||
server.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
#else
|
||||
server.sin_addr.s_addr = inet_addr(basicGetBroadcastAddress());
|
||||
#endif
|
||||
server.sin_port = htons((short)(sp->port & 0xFFFF));
|
||||
if ((bytes = sendto(sp->sock, buf, toWrite, 0,
|
||||
(struct sockaddr *) &server, sizeof(server))) < 0) {
|
||||
bytes = tryAlternateSendTo(sp->sock, buf, toWrite, 0,
|
||||
(struct sockaddr *) &server);
|
||||
}
|
||||
} else if (sp->flags & SOCKET_DATAGRAM) {
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = inet_addr(sp->host);
|
||||
server.sin_port = htons((short)(sp->port & 0xFFFF));
|
||||
bytes = sendto(sp->sock, buf, toWrite, 0,
|
||||
(struct sockaddr *) &server, sizeof(server));
|
||||
|
||||
} else {
|
||||
bytes = send(sp->sock, buf, toWrite, 0);
|
||||
}
|
||||
|
||||
if (bytes < 0) {
|
||||
*errCode = socketGetError();
|
||||
#if WIN || CE
|
||||
sp->currentEvents &= ~FD_WRITE;
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
|
||||
} else if (bytes == 0 && bytes != toWrite) {
|
||||
*errCode = EWOULDBLOCK;
|
||||
#if WIN || CE
|
||||
sp->currentEvents &= ~FD_WRITE;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure we get to write some more data real soon if the socket can absorb
|
||||
* more data
|
||||
*/
|
||||
#if !UEMF
|
||||
#if WIN
|
||||
if (sp->interestEvents & FD_WRITE) {
|
||||
emfTime_t blockTime = { 0, 0 };
|
||||
emfSetMaxBlockTime(&blockTime);
|
||||
}
|
||||
#endif /* WIN */
|
||||
#endif
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* If the sendto failed, swap the first two bytes in the
|
||||
* sockaddr structure. This is a kludge due to a change in
|
||||
* VxWorks between versions 5.3 and 5.4, but we want the
|
||||
* product to run on either.
|
||||
*/
|
||||
static int tryAlternateSendTo(int sock, char *buf, int toWrite, int i,
|
||||
struct sockaddr *server)
|
||||
{
|
||||
#if VXWORKS
|
||||
char *ptr;
|
||||
|
||||
ptr = (char *)server;
|
||||
*ptr = *(ptr+1);
|
||||
*(ptr+1) = 0;
|
||||
return sendto(sock, buf, toWrite, i, server, sizeof(struct sockaddr));
|
||||
#else
|
||||
return -1;
|
||||
#endif /* VXWORKS */
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Allocate a new socket structure
|
||||
*/
|
||||
|
||||
int socketAlloc(char *host, int port, socketAccept_t accept, int flags)
|
||||
{
|
||||
socket_t *sp;
|
||||
int sid;
|
||||
|
||||
if ((sid = hAllocEntry((void***) &socketList, &socketMax,
|
||||
sizeof(socket_t))) < 0) {
|
||||
return -1;
|
||||
}
|
||||
sp = socketList[sid];
|
||||
|
||||
sp->sid = sid;
|
||||
sp->accept = accept;
|
||||
sp->port = port;
|
||||
sp->fileHandle = -1;
|
||||
sp->saveMask = -1;
|
||||
|
||||
if (host) {
|
||||
strncpy(sp->host, host, sizeof(sp->host));
|
||||
}
|
||||
|
||||
/*
|
||||
* Preserve only specified flags from the callers open
|
||||
*/
|
||||
a_assert((flags & ~(SOCKET_BROADCAST|SOCKET_DATAGRAM|SOCKET_BLOCK|
|
||||
SOCKET_LISTENING)) == 0);
|
||||
sp->flags = flags & (SOCKET_BROADCAST | SOCKET_DATAGRAM | SOCKET_BLOCK|
|
||||
SOCKET_LISTENING);
|
||||
|
||||
/*
|
||||
* Add one to allow the user to write exactly SOCKET_BUFSIZ
|
||||
*/
|
||||
ringqOpen(&sp->inBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ);
|
||||
ringqOpen(&sp->outBuf, SOCKET_BUFSIZ + 1, SOCKET_BUFSIZ + 1);
|
||||
ringqOpen(&sp->lineBuf, SOCKET_BUFSIZ, -1);
|
||||
|
||||
return sid;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Free a socket structure
|
||||
*/
|
||||
|
||||
void socketFree(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
char_t buf[256];
|
||||
int i;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* To close a socket, remove any registered interests, set it to
|
||||
* non-blocking so that the recv which follows won't block, do a
|
||||
* shutdown on it so peers on the other end will receive a FIN,
|
||||
* then read any data not yet retrieved from the receive buffer,
|
||||
* and finally close it. If these steps are not all performed
|
||||
* RESETs may be sent to the other end causing problems.
|
||||
*/
|
||||
socketRegisterInterest(sp, 0);
|
||||
if (sp->sock >= 0) {
|
||||
socketSetBlock(sid, 0);
|
||||
if (shutdown(sp->sock, 1) >= 0) {
|
||||
recv(sp->sock, buf, sizeof(buf), 0);
|
||||
}
|
||||
#if WIN || CE
|
||||
closesocket(sp->sock);
|
||||
#else
|
||||
close(sp->sock);
|
||||
#endif
|
||||
}
|
||||
|
||||
ringqClose(&sp->inBuf);
|
||||
ringqClose(&sp->outBuf);
|
||||
ringqClose(&sp->lineBuf);
|
||||
|
||||
bfree(B_L, sp);
|
||||
socketMax = hFree((void***) &socketList, sid);
|
||||
|
||||
/*
|
||||
* Calculate the new highest socket number
|
||||
*/
|
||||
socketHighestFd = -1;
|
||||
for (i = 0; i < socketMax; i++) {
|
||||
if ((sp = socketList[i]) == NULL) {
|
||||
continue;
|
||||
}
|
||||
socketHighestFd = max(socketHighestFd, sp->sock);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Validate a socket handle
|
||||
*/
|
||||
|
||||
socket_t *socketPtr(int sid)
|
||||
{
|
||||
if (sid < 0 || sid >= socketMax || socketList[sid] == NULL) {
|
||||
a_assert(NULL);
|
||||
errno = EBADF;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
a_assert(socketList[sid]);
|
||||
return socketList[sid];
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Get the operating system error code
|
||||
*/
|
||||
|
||||
int socketGetError()
|
||||
{
|
||||
#if WIN || CE
|
||||
switch (WSAGetLastError()) {
|
||||
case WSAEWOULDBLOCK:
|
||||
return EWOULDBLOCK;
|
||||
case WSAECONNRESET:
|
||||
return ECONNRESET;
|
||||
case WSAENETDOWN:
|
||||
return ENETDOWN;
|
||||
case WSAEPROCLIM:
|
||||
return EAGAIN;
|
||||
case WSAEINTR:
|
||||
return EINTR;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Return the underlying socket handle
|
||||
*/
|
||||
|
||||
int socketGetHandle(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return sp->sock;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Get blocking mode
|
||||
*/
|
||||
|
||||
int socketGetBlock(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
a_assert(0);
|
||||
return 0;
|
||||
}
|
||||
return (sp->flags & SOCKET_BLOCK);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Get mode
|
||||
*/
|
||||
|
||||
int socketGetMode(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
a_assert(0);
|
||||
return 0;
|
||||
}
|
||||
return sp->flags;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Set mode
|
||||
*/
|
||||
|
||||
void socketSetMode(int sid, int mode)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
a_assert(0);
|
||||
return;
|
||||
}
|
||||
sp->flags = mode;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Get port.
|
||||
*/
|
||||
|
||||
int socketGetPort(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return sp->port;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
789
cpukit/httpd/sock.c
Normal file
789
cpukit/httpd/sock.c
Normal file
@@ -0,0 +1,789 @@
|
||||
/*
|
||||
* sock.c -- Posix Socket upper layer support module for general posix use
|
||||
*
|
||||
* Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/******************************** Description *********************************/
|
||||
|
||||
/*
|
||||
* Posix Socket Module. This supports blocking and non-blocking buffered
|
||||
* socket I/O.
|
||||
*/
|
||||
|
||||
/********************************** Includes **********************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if UEMF
|
||||
#include "uemf.h"
|
||||
#else
|
||||
#include <socket.h>
|
||||
#include <types.h>
|
||||
#include <unistd.h>
|
||||
#include "emfInternal.h"
|
||||
#endif
|
||||
|
||||
/************************************ Locals **********************************/
|
||||
|
||||
socket_t **socketList; /* List of open sockets */
|
||||
int socketMax; /* Maximum size of socket */
|
||||
int socketHighestFd = -1; /* Highest socket fd opened */
|
||||
|
||||
/***************************** Forward Declarations ***************************/
|
||||
|
||||
static int socketDoOutput(socket_t *sp, char *buf, int toWrite, int *errCode);
|
||||
static int tryAlternateSendTo(int sock, char *buf, int toWrite, int i,
|
||||
struct sockaddr *server);
|
||||
|
||||
/*********************************** Code *************************************/
|
||||
/*
|
||||
* Write to a socket. Absorb as much data as the socket can buffer. Block if
|
||||
* the socket is in blocking mode. Returns -1 on error, otherwise the number
|
||||
* of bytes written.
|
||||
*/
|
||||
|
||||
int socketWrite(int sid, char *buf, int bufsize)
|
||||
{
|
||||
socket_t *sp;
|
||||
ringq_t *rq;
|
||||
int len, bytesWritten, room;
|
||||
|
||||
a_assert(buf);
|
||||
a_assert(bufsize >= 0);
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop adding as much data to the output ringq as we can absorb. Initiate a
|
||||
* flush when the ringq is too full and continue. Block in socketFlush if the
|
||||
* socket is in blocking mode.
|
||||
*/
|
||||
rq = &sp->outBuf;
|
||||
for (bytesWritten = 0; bufsize > 0; ) {
|
||||
if ((room = ringqPutBlkMax(rq)) == 0) {
|
||||
if (socketFlush(sid) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if ((room = ringqPutBlkMax(rq)) == 0) {
|
||||
if (sp->flags & SOCKET_BLOCK) {
|
||||
#if WIN || CE
|
||||
int errCode;
|
||||
if (! socketWaitForEvent(sp, FD_WRITE | SOCKET_WRITABLE,
|
||||
&errCode)) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
len = min(room, bufsize);
|
||||
ringqPutBlk(rq, (unsigned char *) buf, len);
|
||||
bytesWritten += len;
|
||||
bufsize -= len;
|
||||
buf += len;
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Write a string to a socket
|
||||
*/
|
||||
|
||||
int socketWriteString(int sid, char_t *buf)
|
||||
{
|
||||
#if UNICODE
|
||||
char *byteBuf;
|
||||
int r, len;
|
||||
|
||||
len = gstrlen(buf);
|
||||
byteBuf = ballocUniToAsc(buf, len);
|
||||
r = socketWrite(sid, byteBuf, len);
|
||||
bfreeSafe(B_L, byteBuf);
|
||||
return r;
|
||||
#else
|
||||
return socketWrite(sid, buf, strlen(buf));
|
||||
#endif /* UNICODE */
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Read from a socket. Return the number of bytes read if successful. This
|
||||
* may be less than the requested "bufsize" and may be zero. Return -1 for
|
||||
* errors. Return 0 for EOF. Otherwise return the number of bytes read.
|
||||
* If this routine returns zero it indicates an EOF condition.
|
||||
* which can be verified with socketEof()
|
||||
|
||||
* Note: this ignores the line buffer, so a previous socketGets
|
||||
* which read a partial line may cause a subsequent socketRead to miss some
|
||||
* data. This routine may block if the socket is in blocking mode.
|
||||
*
|
||||
*/
|
||||
|
||||
int socketRead(int sid, char *buf, int bufsize)
|
||||
{
|
||||
socket_t *sp;
|
||||
ringq_t *rq;
|
||||
int len, room, errCode, bytesRead;
|
||||
|
||||
a_assert(buf);
|
||||
a_assert(bufsize > 0);
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sp->flags & SOCKET_EOF) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rq = &sp->inBuf;
|
||||
for (bytesRead = 0; bufsize > 0; ) {
|
||||
len = min(ringqLen(rq), bufsize);
|
||||
if (len <= 0) {
|
||||
/*
|
||||
* if blocking mode and already have data, exit now or it may block
|
||||
* forever.
|
||||
*/
|
||||
if ((sp->flags & SOCKET_BLOCK) &&
|
||||
(bytesRead > 0)) {
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* This flush is critical for readers of datagram packets. If the
|
||||
* buffer is not big enough to read the whole datagram in one hit,
|
||||
* the recvfrom call will fail.
|
||||
*/
|
||||
ringqFlush(rq);
|
||||
room = ringqPutBlkMax(rq);
|
||||
len = socketGetInput(sid, (char *) rq->endp, room, &errCode);
|
||||
if (len < 0) {
|
||||
if (errCode == EWOULDBLOCK) {
|
||||
if ((sp->flags & SOCKET_BLOCK) &&
|
||||
(bytesRead == 0)) {
|
||||
continue;
|
||||
}
|
||||
if (bytesRead >= 0) {
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
} else if (len == 0) {
|
||||
/*
|
||||
* If bytesRead is 0, this is EOF since socketRead should never
|
||||
* be called unless there is data yet to be read. Set the flag.
|
||||
* Then pass back the number of bytes read.
|
||||
*/
|
||||
if (bytesRead == 0) {
|
||||
sp->flags |= SOCKET_EOF;
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
ringqPutBlkAdj(rq, len);
|
||||
len = min(len, bufsize);
|
||||
}
|
||||
memcpy(&buf[bytesRead], rq->servp, len);
|
||||
ringqGetBlkAdj(rq, len);
|
||||
bufsize -= len;
|
||||
bytesRead += len;
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Get a string from a socket. This returns data in *buf in a malloced string
|
||||
* after trimming the '\n'. If there is zero bytes returned, *buf will be set
|
||||
* to NULL. If doing non-blocking I/O, it returns -1 for error, EOF or when
|
||||
* no complete line yet read. If doing blocking I/O, it will block until an
|
||||
* entire line is read. If a partial line is read socketInputBuffered or
|
||||
* socketEof can be used to distinguish between EOF and partial line still
|
||||
* buffered. This routine eats and ignores carriage returns.
|
||||
*/
|
||||
|
||||
int socketGets(int sid, char_t **buf)
|
||||
{
|
||||
socket_t *sp;
|
||||
ringq_t *lq;
|
||||
char c;
|
||||
int rc, len;
|
||||
|
||||
a_assert(buf);
|
||||
*buf = NULL;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
lq = &sp->lineBuf;
|
||||
|
||||
while (1) {
|
||||
|
||||
if ((rc = socketRead(sid, &c, 1)) < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
/*
|
||||
* If there is a partial line and we are at EOF, pretend we saw a '\n'
|
||||
*/
|
||||
if (ringqLen(lq) > 0 && (sp->flags & SOCKET_EOF)) {
|
||||
c = '\n';
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If a newline is seen, return the data excluding the new line to the
|
||||
* caller. If carriage return is seen, just eat it.
|
||||
*/
|
||||
if (c == '\n') {
|
||||
len = ringqLen(lq);
|
||||
if (len > 0) {
|
||||
*buf = ballocAscToUni(lq->servp, len);
|
||||
} else {
|
||||
*buf = NULL;
|
||||
}
|
||||
ringqFlush(lq);
|
||||
return len;
|
||||
|
||||
} else if (c == '\r') {
|
||||
continue;
|
||||
}
|
||||
ringqPutcA(lq, c);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Flush the socket. Block if the socket is in blocking mode.
|
||||
* This will return -1 on errors and 0 if successful.
|
||||
*/
|
||||
|
||||
int socketFlush(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
ringq_t *rq;
|
||||
int len, bytesWritten, errCode;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
rq = &sp->outBuf;
|
||||
|
||||
/*
|
||||
* Set the background flushing flag which socketEventProc will check to
|
||||
* continue the flush.
|
||||
*/
|
||||
if (! (sp->flags & SOCKET_BLOCK)) {
|
||||
sp->flags |= SOCKET_FLUSHING;
|
||||
}
|
||||
|
||||
/*
|
||||
* Break from loop if not blocking after initiating output. If we are blocking
|
||||
* we wait for a write event.
|
||||
*/
|
||||
while (ringqLen(rq) > 0) {
|
||||
len = ringqGetBlkMax(&sp->outBuf);
|
||||
bytesWritten = socketDoOutput(sp, (char*) rq->servp, len, &errCode);
|
||||
if (bytesWritten < 0) {
|
||||
if (errCode == EINTR) {
|
||||
continue;
|
||||
} else if (errCode == EWOULDBLOCK || errCode == EAGAIN) {
|
||||
#if WIN || CE
|
||||
if (sp->flags & SOCKET_BLOCK) {
|
||||
int errCode;
|
||||
if (! socketWaitForEvent(sp, FD_WRITE | SOCKET_WRITABLE,
|
||||
&errCode)) {
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Ensure we get a FD_WRITE message when the socket can absorb
|
||||
* more data (non-blocking only.) Store the user's mask if we
|
||||
* haven't done it already.
|
||||
*/
|
||||
if (sp->saveMask < 0 ) {
|
||||
sp->saveMask = sp->handlerMask;
|
||||
socketRegisterInterest(sp,
|
||||
sp->handlerMask | SOCKET_WRITABLE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
ringqGetBlkAdj(rq, bytesWritten);
|
||||
}
|
||||
/*
|
||||
* If the buffer is empty, reset the ringq pointers to point to the start
|
||||
* of the buffer. This is essential to ensure that datagrams get written
|
||||
* in one single I/O operation.
|
||||
*/
|
||||
if (ringqLen(rq) == 0) {
|
||||
ringqFlush(rq);
|
||||
}
|
||||
/*
|
||||
* Restore the users mask if it was saved by the non-blocking code above.
|
||||
* Note: saveMask = -1 if empty. socketRegisterInterest will set handlerMask
|
||||
*/
|
||||
if (sp->saveMask >= 0) {
|
||||
socketRegisterInterest(sp, sp->saveMask);
|
||||
sp->saveMask = -1;
|
||||
}
|
||||
sp->flags &= ~SOCKET_FLUSHING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Return the count of input characters buffered. We look at both the line
|
||||
* buffer and the input (raw) buffer. Return -1 on error or EOF.
|
||||
*/
|
||||
|
||||
int socketInputBuffered(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (socketEof(sid)) {
|
||||
return -1;
|
||||
}
|
||||
return ringqLen(&sp->lineBuf) + ringqLen(&sp->inBuf);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Return true if EOF
|
||||
*/
|
||||
|
||||
int socketEof(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return sp->flags & SOCKET_EOF;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Return the number of bytes the socket can absorb without blocking
|
||||
*/
|
||||
|
||||
int socketCanWrite(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return sp->outBuf.buflen - ringqLen(&sp->outBuf) - 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Add one to allow the user to write exactly SOCKET_BUFSIZ
|
||||
*/
|
||||
|
||||
void socketSetBufferSize(int sid, int in, int line, int out)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (in >= 0) {
|
||||
ringqClose(&sp->inBuf);
|
||||
in++;
|
||||
ringqOpen(&sp->inBuf, in, in);
|
||||
}
|
||||
|
||||
if (line >= 0) {
|
||||
ringqClose(&sp->lineBuf);
|
||||
line++;
|
||||
ringqOpen(&sp->lineBuf, line, line);
|
||||
}
|
||||
|
||||
if (out >= 0) {
|
||||
ringqClose(&sp->outBuf);
|
||||
out++;
|
||||
ringqOpen(&sp->outBuf, out, out);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Create a user handler for this socket. The handler called whenever there
|
||||
* is an event of interest as defined by handlerMask (SOCKET_READABLE, ...)
|
||||
*/
|
||||
|
||||
void socketCreateHandler(int sid, int handlerMask, socketHandler_t handler,
|
||||
int data)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return;
|
||||
}
|
||||
sp->handler = handler;
|
||||
sp->handler_data = data;
|
||||
socketRegisterInterest(sp, handlerMask);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Delete a handler
|
||||
*/
|
||||
|
||||
void socketDeleteHandler(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return;
|
||||
}
|
||||
sp->handler = NULL;
|
||||
socketRegisterInterest(sp, 0);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Socket output procedure. Return -1 on errors otherwise return the number
|
||||
* of bytes written.
|
||||
*/
|
||||
|
||||
static int socketDoOutput(socket_t *sp, char *buf, int toWrite, int *errCode)
|
||||
{
|
||||
struct sockaddr_in server;
|
||||
int bytes;
|
||||
|
||||
a_assert(sp);
|
||||
a_assert(buf);
|
||||
a_assert(toWrite > 0);
|
||||
a_assert(errCode);
|
||||
|
||||
*errCode = 0;
|
||||
|
||||
#if WIN || CE
|
||||
if ((sp->flags & SOCKET_ASYNC)
|
||||
&& ! socketWaitForEvent(sp, FD_CONNECT, errCode)) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Write the data
|
||||
*/
|
||||
if (sp->flags & SOCKET_BROADCAST) {
|
||||
server.sin_family = AF_INET;
|
||||
#if UEMF || LITTLEFOOT
|
||||
server.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
#else
|
||||
server.sin_addr.s_addr = inet_addr(basicGetBroadcastAddress());
|
||||
#endif
|
||||
server.sin_port = htons((short)(sp->port & 0xFFFF));
|
||||
if ((bytes = sendto(sp->sock, buf, toWrite, 0,
|
||||
(struct sockaddr *) &server, sizeof(server))) < 0) {
|
||||
bytes = tryAlternateSendTo(sp->sock, buf, toWrite, 0,
|
||||
(struct sockaddr *) &server);
|
||||
}
|
||||
} else if (sp->flags & SOCKET_DATAGRAM) {
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = inet_addr(sp->host);
|
||||
server.sin_port = htons((short)(sp->port & 0xFFFF));
|
||||
bytes = sendto(sp->sock, buf, toWrite, 0,
|
||||
(struct sockaddr *) &server, sizeof(server));
|
||||
|
||||
} else {
|
||||
bytes = send(sp->sock, buf, toWrite, 0);
|
||||
}
|
||||
|
||||
if (bytes < 0) {
|
||||
*errCode = socketGetError();
|
||||
#if WIN || CE
|
||||
sp->currentEvents &= ~FD_WRITE;
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
|
||||
} else if (bytes == 0 && bytes != toWrite) {
|
||||
*errCode = EWOULDBLOCK;
|
||||
#if WIN || CE
|
||||
sp->currentEvents &= ~FD_WRITE;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure we get to write some more data real soon if the socket can absorb
|
||||
* more data
|
||||
*/
|
||||
#if !UEMF
|
||||
#if WIN
|
||||
if (sp->interestEvents & FD_WRITE) {
|
||||
emfTime_t blockTime = { 0, 0 };
|
||||
emfSetMaxBlockTime(&blockTime);
|
||||
}
|
||||
#endif /* WIN */
|
||||
#endif
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* If the sendto failed, swap the first two bytes in the
|
||||
* sockaddr structure. This is a kludge due to a change in
|
||||
* VxWorks between versions 5.3 and 5.4, but we want the
|
||||
* product to run on either.
|
||||
*/
|
||||
static int tryAlternateSendTo(int sock, char *buf, int toWrite, int i,
|
||||
struct sockaddr *server)
|
||||
{
|
||||
#if VXWORKS
|
||||
char *ptr;
|
||||
|
||||
ptr = (char *)server;
|
||||
*ptr = *(ptr+1);
|
||||
*(ptr+1) = 0;
|
||||
return sendto(sock, buf, toWrite, i, server, sizeof(struct sockaddr));
|
||||
#else
|
||||
return -1;
|
||||
#endif /* VXWORKS */
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Allocate a new socket structure
|
||||
*/
|
||||
|
||||
int socketAlloc(char *host, int port, socketAccept_t accept, int flags)
|
||||
{
|
||||
socket_t *sp;
|
||||
int sid;
|
||||
|
||||
if ((sid = hAllocEntry((void***) &socketList, &socketMax,
|
||||
sizeof(socket_t))) < 0) {
|
||||
return -1;
|
||||
}
|
||||
sp = socketList[sid];
|
||||
|
||||
sp->sid = sid;
|
||||
sp->accept = accept;
|
||||
sp->port = port;
|
||||
sp->fileHandle = -1;
|
||||
sp->saveMask = -1;
|
||||
|
||||
if (host) {
|
||||
strncpy(sp->host, host, sizeof(sp->host));
|
||||
}
|
||||
|
||||
/*
|
||||
* Preserve only specified flags from the callers open
|
||||
*/
|
||||
a_assert((flags & ~(SOCKET_BROADCAST|SOCKET_DATAGRAM|SOCKET_BLOCK|
|
||||
SOCKET_LISTENING)) == 0);
|
||||
sp->flags = flags & (SOCKET_BROADCAST | SOCKET_DATAGRAM | SOCKET_BLOCK|
|
||||
SOCKET_LISTENING);
|
||||
|
||||
/*
|
||||
* Add one to allow the user to write exactly SOCKET_BUFSIZ
|
||||
*/
|
||||
ringqOpen(&sp->inBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ);
|
||||
ringqOpen(&sp->outBuf, SOCKET_BUFSIZ + 1, SOCKET_BUFSIZ + 1);
|
||||
ringqOpen(&sp->lineBuf, SOCKET_BUFSIZ, -1);
|
||||
|
||||
return sid;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Free a socket structure
|
||||
*/
|
||||
|
||||
void socketFree(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
char_t buf[256];
|
||||
int i;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* To close a socket, remove any registered interests, set it to
|
||||
* non-blocking so that the recv which follows won't block, do a
|
||||
* shutdown on it so peers on the other end will receive a FIN,
|
||||
* then read any data not yet retrieved from the receive buffer,
|
||||
* and finally close it. If these steps are not all performed
|
||||
* RESETs may be sent to the other end causing problems.
|
||||
*/
|
||||
socketRegisterInterest(sp, 0);
|
||||
if (sp->sock >= 0) {
|
||||
socketSetBlock(sid, 0);
|
||||
if (shutdown(sp->sock, 1) >= 0) {
|
||||
recv(sp->sock, buf, sizeof(buf), 0);
|
||||
}
|
||||
#if WIN || CE
|
||||
closesocket(sp->sock);
|
||||
#else
|
||||
close(sp->sock);
|
||||
#endif
|
||||
}
|
||||
|
||||
ringqClose(&sp->inBuf);
|
||||
ringqClose(&sp->outBuf);
|
||||
ringqClose(&sp->lineBuf);
|
||||
|
||||
bfree(B_L, sp);
|
||||
socketMax = hFree((void***) &socketList, sid);
|
||||
|
||||
/*
|
||||
* Calculate the new highest socket number
|
||||
*/
|
||||
socketHighestFd = -1;
|
||||
for (i = 0; i < socketMax; i++) {
|
||||
if ((sp = socketList[i]) == NULL) {
|
||||
continue;
|
||||
}
|
||||
socketHighestFd = max(socketHighestFd, sp->sock);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Validate a socket handle
|
||||
*/
|
||||
|
||||
socket_t *socketPtr(int sid)
|
||||
{
|
||||
if (sid < 0 || sid >= socketMax || socketList[sid] == NULL) {
|
||||
a_assert(NULL);
|
||||
errno = EBADF;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
a_assert(socketList[sid]);
|
||||
return socketList[sid];
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Get the operating system error code
|
||||
*/
|
||||
|
||||
int socketGetError()
|
||||
{
|
||||
#if WIN || CE
|
||||
switch (WSAGetLastError()) {
|
||||
case WSAEWOULDBLOCK:
|
||||
return EWOULDBLOCK;
|
||||
case WSAECONNRESET:
|
||||
return ECONNRESET;
|
||||
case WSAENETDOWN:
|
||||
return ENETDOWN;
|
||||
case WSAEPROCLIM:
|
||||
return EAGAIN;
|
||||
case WSAEINTR:
|
||||
return EINTR;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Return the underlying socket handle
|
||||
*/
|
||||
|
||||
int socketGetHandle(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return sp->sock;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Get blocking mode
|
||||
*/
|
||||
|
||||
int socketGetBlock(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
a_assert(0);
|
||||
return 0;
|
||||
}
|
||||
return (sp->flags & SOCKET_BLOCK);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Get mode
|
||||
*/
|
||||
|
||||
int socketGetMode(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
a_assert(0);
|
||||
return 0;
|
||||
}
|
||||
return sp->flags;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Set mode
|
||||
*/
|
||||
|
||||
void socketSetMode(int sid, int mode)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
a_assert(0);
|
||||
return;
|
||||
}
|
||||
sp->flags = mode;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Get port.
|
||||
*/
|
||||
|
||||
int socketGetPort(int sid)
|
||||
{
|
||||
socket_t *sp;
|
||||
|
||||
if ((sp = socketPtr(sid)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return sp->port;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
Reference in New Issue
Block a user