From 527b508c9416cf93b7ad3bfaf33d7649bfb19a3a Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Thu, 28 Sep 2000 20:08:07 +0000 Subject: 2000-09-28 Joel Sherrill * 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. --- cpukit/httpd/sock.c | 789 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 789 insertions(+) create mode 100644 cpukit/httpd/sock.c (limited to 'cpukit/httpd') diff --git a/cpukit/httpd/sock.c b/cpukit/httpd/sock.c new file mode 100644 index 0000000000..f23a63984f --- /dev/null +++ b/cpukit/httpd/sock.c @@ -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 +#include + +#if UEMF + #include "uemf.h" +#else + #include + #include + #include + #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; +} + +/******************************************************************************/ -- cgit v1.2.3