/* * sockGen.c -- Posix Socket 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. */ #if (!WIN) || LITTLEFOOT || WEBS /********************************** Includes **********************************/ #include #include #if UEMF #include "uemf.h" #else #include #include #include #include "emfInternal.h" #endif #if VXWORKS #include #endif #if __rtems__ #include #endif /************************************ Locals **********************************/ extern socket_t **socketList; /* List of open sockets */ extern int socketMax; /* Maximum size of socket */ extern int socketHighestFd; /* Highest socket fd opened */ static int socketOpenCount = 0; /* Number of task using sockets */ /***************************** Forward Declarations ***************************/ static void socketAccept(socket_t *sp); static int socketDoEvent(socket_t *sp); static int tryAlternateConnect(int sock, struct sockaddr *sockaddr); /*********************************** Code *************************************/ /* * Open socket module */ int socketOpen() { #if CE || WIN WSADATA wsaData; #endif if (++socketOpenCount > 1) { return 0; } #if CE || WIN if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) { return -1; } if (wsaData.wVersion != MAKEWORD(1,1)) { WSACleanup(); return -1; } #endif socketList = NULL; socketMax = 0; socketHighestFd = -1; return 0; } /******************************************************************************/ /* * Close the socket module, by closing all open connections */ void socketClose() { int i; if (--socketOpenCount <= 0) { for (i = socketMax; i >= 0; i--) { if (socketList && socketList[i]) { socketCloseConnection(i); } } socketOpenCount = 0; } } /******************************************************************************/ /* * Open a client or server socket. Host is NULL if we want server capability. */ int socketOpenConnection(char *host, int port, socketAccept_t accept, int flags) { #if ! (NO_GETHOSTBYNAME || VXWORKS) struct hostent *hostent; /* Host database entry */ #endif /* ! (NO_GETHOSTBYNAME || VXWORKS) */ socket_t *sp; struct sockaddr_in sockaddr; int sid, bcast, dgram, rc; if (port > SOCKET_PORT_MAX) { return -1; } /* * Allocate a socket structure */ if ((sid = socketAlloc(host, port, accept, flags)) < 0) { return -1; } sp = socketList[sid]; a_assert(sp); /* * Create the socket address structure */ memset((char *) &sockaddr, '\0', sizeof(struct sockaddr_in)); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons((short) (port & 0xFFFF)); if (host == NULL) { sockaddr.sin_addr.s_addr = INADDR_ANY; } else { sockaddr.sin_addr.s_addr = inet_addr(host); if (sockaddr.sin_addr.s_addr == INADDR_NONE) { /* * If the OS does not support gethostbyname functionality, the macro: * NO_GETHOSTBYNAME should be defined to skip the use of gethostbyname. * Unfortunatly there is no easy way to recover, the following code * simply uses the basicGetHost IP for the sockaddr. */ #if NO_GETHOSTBYNAME if (strcmp(host, basicGetHost()) == 0) { sockaddr.sin_addr.s_addr = inet_addr(basicGetAddress()); } if (sockaddr.sin_addr.s_addr == INADDR_NONE) { socketFree(sid); return -1; } #elif VXWORKS sockaddr.sin_addr.s_addr = (unsigned long) hostGetByName(host); if (sockaddr.sin_addr.s_addr == NULL) { errno = ENXIO; socketFree(sid); return -1; } #else hostent = gethostbyname(host); if (hostent != NULL) { memcpy((char *) &sockaddr.sin_addr, (char *) hostent->h_addr_list[0], (size_t) hostent->h_length); } else { char *asciiAddress; char_t *address; address = basicGetAddress(); asciiAddress = ballocUniToAsc(address, gstrlen(address)); sockaddr.sin_addr.s_addr = inet_addr(asciiAddress); bfree(B_L, asciiAddress); if (sockaddr.sin_addr.s_addr == INADDR_NONE) { errno = ENXIO; socketFree(sid); return -1; } } #endif /* (NO_GETHOSTBYNAME || VXWORKS) */ } } bcast = sp->flags & SOCKET_BROADCAST; if (bcast) { sp->flags |= SOCKET_DATAGRAM; } dgram = sp->flags & SOCKET_DATAGRAM; /* * Create the socket. Support for datagram sockets. Set the close on * exec flag so children don't inherit the socket. */ sp->sock = socket(AF_INET, dgram ? SOCK_DGRAM: SOCK_STREAM, 0); if (sp->sock < 0) { socketFree(sid); return -1; } #ifndef __NO_FCNTL fcntl(sp->sock, F_SETFD, FD_CLOEXEC); #endif socketHighestFd = max(socketHighestFd, sp->sock); /* * If broadcast, we need to turn on broadcast capability. */ if (bcast) { int broadcastFlag = 1; if (setsockopt(sp->sock, SOL_SOCKET, SO_BROADCAST, (char *) &broadcastFlag, sizeof(broadcastFlag)) < 0) { socketFree(sid); return -1; } } /* * Host is set if we are the client */ if (host) { /* * Connect to the remote server in blocking mode, then go into * non-blocking mode if desired. */ if (!dgram) { if (! (sp->flags & SOCKET_BLOCK)) { /* * sockGen.c is only used for Windows products when blocking * connects are expected. This applies to FieldUpgrader * agents and open source webserver connectws. Therefore the * asynchronous connect code here is not compiled. */ #if (WIN || CE) && !(LITTLEFOOT || WEBS) int flag; sp->flags |= SOCKET_ASYNC; /* * Set to non-blocking for an async connect */ flag = 1; if (ioctlsocket(sp->sock, FIONBIO, &flag) == SOCKET_ERROR) { socketFree(sid); return -1; } #else socketSetBlock(sid, 1); #endif /* #if (WIN || CE) && !(LITTLEFOOT || WEBS) */ } if ((rc = connect(sp->sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) < 0 && (rc = tryAlternateConnect(sp->sock, (struct sockaddr *) &sockaddr)) < 0) { #if WIN || CE if (socketGetError() != EWOULDBLOCK) { socketFree(sid); return -1; } #else socketFree(sid); return -1; #endif /* WIN || CE */ } } } else { /* * Bind to the socket endpoint and the call listen() to start listening */ rc = 1; setsockopt(sp->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc)); if (bind(sp->sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) < 0) { socketFree(sid); return -1; } if (! dgram) { if (listen(sp->sock, SOMAXCONN) < 0) { socketFree(sid); return -1; } #if !UEMF sp->fileHandle = emfCreateFileHandler(sp->sock, SOCKET_READABLE, (emfFileProc *) socketAccept, (void *) sp); #else sp->flags |= SOCKET_LISTENING; #endif } sp->handlerMask |= SOCKET_READABLE; } /* * Set the blocking mode */ if (flags & SOCKET_BLOCK) { socketSetBlock(sid, 1); } else { socketSetBlock(sid, 0); } return sid; } /******************************************************************************/ /* * If the connection 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 tryAlternateConnect(int sock, struct sockaddr *sockaddr) { #if VXWORKS char *ptr; ptr = (char *)sockaddr; *ptr = *(ptr+1); *(ptr+1) = 0; return connect(sock, sockaddr, sizeof(struct sockaddr)); #else return -1; #endif /* VXWORKS */ } /******************************************************************************/ /* * Close a socket */ void socketCloseConnection(int sid) { socket_t *sp; if ((sp = socketPtr(sid)) == NULL) { return; } socketFree(sid); } /******************************************************************************/ /* * Accept a connection. Called as a callback on incoming connection. */ static void socketAccept(socket_t *sp) { struct sockaddr_in addr; socket_t *nsp; size_t len; char *pString; int newSock, nid; a_assert(sp); /* * Accept the connection and prevent inheriting by children (F_SETFD) */ len = sizeof(struct sockaddr_in); if ((newSock = accept(sp->sock, (struct sockaddr *) &addr, &len)) < 0) { return; } #ifndef __NO_FCNTL fcntl(newSock, F_SETFD, FD_CLOEXEC); #endif socketHighestFd = max(socketHighestFd, newSock); /* * Create a socket structure and insert into the socket list */ nid = socketAlloc(sp->host, sp->port, sp->accept, sp->flags); nsp = socketList[nid]; a_assert(nsp); nsp->sock = newSock; nsp->flags &= ~SOCKET_LISTENING; if (nsp == NULL) { return; } /* * Set the blocking mode before calling the accept callback. */ socketSetBlock(nid, (nsp->flags & SOCKET_BLOCK) ? 1: 0); /* * Call the user accept callback. The user must call socketCreateHandler * to register for further events of interest. */ if (sp->accept != NULL) { pString = inet_ntoa(addr.sin_addr); if ((sp->accept)(nid, pString, ntohs(addr.sin_port), sp->sid) < 0) { socketFree(nid); } #if VXWORKS free(pString); #endif } } /******************************************************************************/ /* * Get more input from the socket and return in buf. * Returns 0 for EOF, -1 for errors and otherwise the number of bytes read. */ int socketGetInput(int sid, char *buf, int toRead, int *errCode) { struct sockaddr_in server; socket_t *sp; int len, bytesRead; a_assert(buf); a_assert(errCode); *errCode = 0; if ((sp = socketPtr(sid)) == NULL) { return -1; } /* * If we have previously seen an EOF condition, then just return */ if (sp->flags & SOCKET_EOF) { return 0; } #if (WIN || CE) && !(LITTLEFOOT || WEBS) if ( !(sp->flags & SOCKET_BLOCK) && ! socketWaitForEvent(sp, FD_CONNECT, errCode)) { return -1; } #endif /* * Read the data */ if (sp->flags & SOCKET_DATAGRAM) { len = sizeof(server); bytesRead = recvfrom(sp->sock, buf, toRead, 0, (struct sockaddr *) &server, &len); } else { bytesRead = recv(sp->sock, buf, toRead, 0); } if (bytesRead < 0) { if (errno == ECONNRESET) { return 0; } *errCode = socketGetError(); return -1; } return bytesRead; } /******************************************************************************/ /* * Process an event on the event queue */ #ifndef UEMF static int socketEventProc(void *data, int mask) { socket_t *sp; ringq_t *rq; int sid; sid = (int) data; a_assert(sid >= 0 && sid < socketMax); a_assert(socketList[sid]); if ((sp = socketPtr(sid)) == NULL) { return 1; } /* * If now writable and flushing in the background, continue flushing */ if (mask & SOCKET_WRITABLE) { if (sp->flags & SOCKET_FLUSHING) { rq = &sp->outBuf; if (ringqLen(rq) > 0) { socketFlush(sp->sid); } else { sp->flags &= ~SOCKET_FLUSHING; } } } /* * Now invoke the users socket handler. NOTE: the handler may delete the * socket, so we must be very careful after calling the handler. */ if (sp->handler && (sp->handlerMask & mask)) { (sp->handler)(sid, mask & sp->handlerMask, sp->handler_data); } if (socketList && sid < socketMax && socketList[sid] == sp) { socketRegisterInterest(sp, sp->handlerMask); } return 1; } #endif /* ! UEMF */ /******************************************************************************/ /* * Define the events of interest */ void socketRegisterInterest(socket_t *sp, int handlerMask) { a_assert(sp); sp->handlerMask = handlerMask; #if !UEMF if (handlerMask) { sp->fileHandle = emfCreateFileHandler(sp->sock, handlerMask, (emfFileProc *) socketEventProc, (void *) sp->sid); } else { emfDeleteFileHandler(sp->fileHandle); } #endif /* ! UEMF */ } /******************************************************************************/ /* * Wait until an event occurs on a socket. Return 1 on success, 0 on failure. * or -1 on exception (UEMF only) */ int socketWaitForEvent(socket_t *sp, int handlerMask, int *errCode) { int mask; a_assert(sp); mask = sp->handlerMask; sp->handlerMask |= handlerMask; while (socketSelect(sp->sid, 1000)) { if (sp->currentEvents & (handlerMask | SOCKET_EXCEPTION)) { break; } } sp->handlerMask = mask; if (sp->currentEvents & SOCKET_EXCEPTION) { return -1; } else if (sp->currentEvents & handlerMask) { return 1; } if (errCode) { *errCode = errno = EWOULDBLOCK; } return 0; } /******************************************************************************/ /* * Return TRUE if there is a socket with an event ready to process, */ int socketReady(int sid) { socket_t *sp; int all; all = 0; if (sid < 0) { sid = 0; all = 1; } for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { if (! all) { break; } else { continue; } } if (sp->currentEvents & sp->handlerMask) { return 1; } /* * If there is input data, also call select to test for new events */ if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid)) { socketSelect(sid, 0); return 1; } if (! all) { break; } } return 0; } /******************************************************************************/ /* * Wait for a handle to become readable or writable and return a number of * noticed events. Timeout is in milliseconds. */ #if WIN || CE int socketSelect(int sid, int timeout) { struct timeval tv; socket_t *sp; fd_set readFds, writeFds, exceptFds; int nEvents; int all, socketHighestFd; /* Highest socket fd opened */ FD_ZERO(&readFds); FD_ZERO(&writeFds); FD_ZERO(&exceptFds); socketHighestFd = -1; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; /* * Set the select event masks for events to watch */ all = nEvents = 0; if (sid < 0) { all++; sid = 0; } for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { continue; } a_assert(sp); /* * Set the appropriate bit in the ready masks for the sp->sock. */ if (sp->handlerMask & SOCKET_READABLE) { FD_SET(sp->sock, &readFds); nEvents++; if (socketInputBuffered(sid) > 0) { tv.tv_sec = 0; tv.tv_usec = 0; } } if (sp->handlerMask & SOCKET_WRITABLE) { FD_SET(sp->sock, &writeFds); nEvents++; } if (sp->handlerMask & SOCKET_EXCEPTION) { FD_SET(sp->sock, &exceptFds); nEvents++; } if (! all) { break; } } /* * Windows select() fails if no descriptors are set, instead of just sleeping * like other, nice select() calls. So, if WIN, sleep. */ if (nEvents == 0) { Sleep(timeout); return 0; } /* * Wait for the event or a timeout. */ nEvents = select(socketHighestFd+1, &readFds, &writeFds, &exceptFds, &tv); if (all) { sid = 0; } for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { continue; } if (FD_ISSET(sp->sock, &readFds) || socketInputBuffered(sid) > 0) { sp->currentEvents |= SOCKET_READABLE; } if (FD_ISSET(sp->sock, &writeFds)) { sp->currentEvents |= SOCKET_WRITABLE; } if (FD_ISSET(sp->sock, &exceptFds)) { sp->currentEvents |= SOCKET_EXCEPTION; } if (! all) { break; } } return nEvents; } #else /* not WIN || CE */ int socketSelect(int sid, int timeout) { socket_t *sp; struct timeval tv; fd_mask *readFds, *writeFds, *exceptFds; int all, len, nwords, index, bit, nEvents; /* * Allocate and zero the select masks */ nwords = (socketHighestFd + NFDBITS) / NFDBITS; len = nwords * sizeof(int); readFds = balloc(B_L, len); memset(readFds, 0, len); writeFds = balloc(B_L, len); memset(writeFds, 0, len); exceptFds = balloc(B_L, len); memset(exceptFds, 0, len); tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; /* * Set the select event masks for events to watch */ all = nEvents = 0; if (sid < 0) { all++; sid = 0; } for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { if (all == 0) { break; } else { continue; } } a_assert(sp); /* * Initialize the ready masks and compute the mask offsets. */ index = sp->sock / (NBBY * sizeof(fd_mask)); bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask))); /* * Set the appropriate bit in the ready masks for the sp->sock. */ if (sp->handlerMask & SOCKET_READABLE) { readFds[index] |= bit; nEvents++; if (socketInputBuffered(sid) > 0) { tv.tv_sec = 0; tv.tv_usec = 0; } } if (sp->handlerMask & SOCKET_WRITABLE) { writeFds[index] |= bit; nEvents++; } if (sp->handlerMask & SOCKET_EXCEPTION) { exceptFds[index] |= bit; nEvents++; } if (! all) { break; } } /* * Wait for the event or a timeout. Reset nEvents to be the number of actual * events now. */ nEvents = select(socketHighestFd + 1, (fd_set *) readFds, (fd_set *) writeFds, (fd_set *) exceptFds, &tv); if (nEvents > 0) { if (all) { sid = 0; } for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { if (all == 0) { break; } else { continue; } } index = sp->sock / (NBBY * sizeof(fd_mask)); bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask))); if (readFds[index] & bit || socketInputBuffered(sid) > 0) { sp->currentEvents |= SOCKET_READABLE; } if (writeFds[index] & bit) { sp->currentEvents |= SOCKET_WRITABLE; } if (exceptFds[index] & bit) { sp->currentEvents |= SOCKET_EXCEPTION; } if (! all) { break; } } } bfree(B_L, readFds); bfree(B_L, writeFds); bfree(B_L, exceptFds); return nEvents; } #endif /* WIN || CE */ /******************************************************************************/ /* * Process socket events */ void socketProcess(int sid) { socket_t *sp; int all; all = 0; if (sid < 0) { all = 1; sid = 0; } /* * Process each socket */ for (; sid < socketMax; sid++) { if ((sp = socketList[sid]) == NULL) { if (! all) { break; } else { continue; } } if (socketReady(sid)) { socketDoEvent(sp); } if (! all) { break; } } } /******************************************************************************/ /* * Process an event on the event queue */ static int socketDoEvent(socket_t *sp) { ringq_t *rq; int sid; a_assert(sp); sid = sp->sid; if (sp->currentEvents & SOCKET_READABLE) { if (sp->flags & SOCKET_LISTENING) { socketAccept(sp); sp->currentEvents = 0; return 1; } } else { /* * If there is still read data in the buffers, trigger the read handler * NOTE: this may busy spin if the read handler doesn't read the data */ if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid)) { sp->currentEvents |= SOCKET_READABLE; } } /* * If now writable and flushing in the background, continue flushing */ if (sp->currentEvents & SOCKET_WRITABLE) { if (sp->flags & SOCKET_FLUSHING) { rq = &sp->outBuf; if (ringqLen(rq) > 0) { socketFlush(sp->sid); } else { sp->flags &= ~SOCKET_FLUSHING; } } } /* * Now invoke the users socket handler. NOTE: the handler may delete the * socket, so we must be very careful after calling the handler. */ if (sp->handler && (sp->handlerMask & sp->currentEvents)) { (sp->handler)(sid, sp->handlerMask & sp->currentEvents, sp->handler_data); /* * Make sure socket pointer is still valid, then reset the currentEvents. */ if (socketList && sid < socketMax && socketList[sid] == sp) { sp->currentEvents = 0; } } return 1; } /******************************************************************************/ /* * Set the socket blocking mode */ int socketSetBlock(int sid, int on) { socket_t *sp; unsigned long flag; int iflag; int oldBlock; flag = iflag = !on; if ((sp = socketPtr(sid)) == NULL) { a_assert(0); return 0; } oldBlock = (sp->flags & SOCKET_BLOCK); sp->flags &= ~(SOCKET_BLOCK); if (on) { sp->flags |= SOCKET_BLOCK; } /* * Put the socket into block / non-blocking mode */ if (sp->flags & SOCKET_BLOCK) { #if CE || WIN ioctlsocket(sp->sock, FIONBIO, &flag); #elif ECOS int off; off = 0; ioctl(sp->sock, FIONBIO, &off); #elif VXWORKS ioctl(sp->sock, FIONBIO, (int)&iflag); #else fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) & ~O_NONBLOCK); #endif } else { #if CE || WIN ioctlsocket(sp->sock, FIONBIO, &flag); #elif ECOS int on; on = 1; ioctl(sp->sock, FIONBIO, &on); #elif VXWORKS ioctl(sp->sock, FIONBIO, (int)&iflag); #else fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) | O_NONBLOCK); #endif } return oldBlock; } /******************************************************************************/ /* * Return true if a readable socket has buffered data. - not public */ int socketDontBlock() { socket_t *sp; int i; for (i = 0; i < socketMax; i++) { if ((sp = socketList[i]) == NULL || (sp->handlerMask & SOCKET_READABLE) == 0) { continue; } if (socketInputBuffered(i) > 0) { return 1; } } return 0; } /******************************************************************************/ /* * Return true if a particular socket buffered data. - not public */ int socketSockBuffered(int sock) { socket_t *sp; int i; for (i = 0; i < socketMax; i++) { if ((sp = socketList[i]) == NULL || sp->sock != sock) { continue; } return socketInputBuffered(i); } return 0; } #endif /* (!WIN) | LITTLEFOOT | WEBS */ /******************************************************************************/