/** Read data from an outbound stream-oriented connection. * * @param[in] socklist - The list of sockets being monitored. * @param[in] sock - The socket to be read from. */ void OutgoingStreamRead(SLPList * socklist, SLPDSocket * sock) { int bytesread; char peek[16]; socklen_t peeraddrlen = sizeof(struct sockaddr_storage); unsigned int msglen; if (sock->state == STREAM_READ_FIRST) { /*---------------------------------------------------*/ /* take a peek at the packet to get size information */ /*---------------------------------------------------*/ bytesread = recvfrom(sock->fd, peek, 16, MSG_PEEK, (struct sockaddr *) &(sock->peeraddr), &peeraddrlen); if (bytesread > 0) { /* allocate the recvbuf big enough for the whole message */ msglen = PEEK_LENGTH(peek); sock->recvbuf = SLPBufferRealloc(sock->recvbuf, msglen); if (sock->recvbuf) sock->state = STREAM_READ; else { SLPDLog("INTERNAL_ERROR - out of memory!\n"); sock->state = SOCKET_CLOSE; } } else if (bytesread == -1) { #ifdef _WIN32 if (WSAEWOULDBLOCK != WSAGetLastError()) #else if (errno != EWOULDBLOCK) #endif { /* Error occured or connection was closed. Try to reconnect */ /* Socket will be closed if connect times out */ OutgoingStreamReconnect(socklist, sock); } } else { sock->state = SOCKET_CLOSE; } } if (sock->state == STREAM_READ) { /*------------------------------*/ /* recv the rest of the message */ /*------------------------------*/ bytesread = recv(sock->fd, (char *)sock->recvbuf->curpos, (int)(sock->recvbuf->end - sock->recvbuf->curpos), 0); if (bytesread > 0) { /* reset age because of activity */ sock->age = 0; /* move buffer pointers */ sock->recvbuf->curpos += bytesread; /* check to see if everything was read */ if (sock->recvbuf->curpos == sock->recvbuf->end) switch (SLPDProcessMessage(&(sock->peeraddr), &(sock->localaddr), sock->recvbuf, &(sock->sendbuf), 0)) { case SLP_ERROR_DA_BUSY_NOW: sock->state = STREAM_WRITE_WAIT; break; case SLP_ERROR_PARSE_ERROR: case SLP_ERROR_VER_NOT_SUPPORTED: sock->state = SOCKET_CLOSE; break; default: /* End of outgoing message exchange. Unlink */ /* send buf from to do list and free it */ SLPBufferFree(sock->sendbuf); sock->sendbuf = NULL; sock->state = STREAM_WRITE_FIRST; /* clear the reconnection count since we actually * transmitted a successful message exchange */ sock->reconns = 0; break; } } else { #ifdef _WIN32 if (WSAEWOULDBLOCK != WSAGetLastError()) #else if (errno != EWOULDBLOCK) #endif { /* Error occured or connection was closed. Try to reconnect */ /* Socket will be closed if connect times out */ OutgoingStreamReconnect(socklist, sock); } } } }
/** Receives a message. * * @param[in] sockfd - The pre-configured socket on which to read. * @param[in] socktype - The socket type (stream or datagram). * @param[out] buf - The buffer into which a message should be read. * @param[out] peeraddr - The address of storage for the remote address. * @param[in] timeout - The maximum time to wait for network operations. * * @return Zero on success, or non-zero on failure with errno set. Values * for errno include ENOTCONN on read error, ETIMEOUT on network timeout, * ENOMEM on out-of-memory error, and EINVAL on parse error. */ int SLPNetworkRecvMessage(sockfd_t sockfd, int socktype, SLPBuffer * buf, void * peeraddr, struct timeval * timeout) { int xferbytes, recvlen; #if HAVE_POLL struct pollfd readfd; #else fd_set readfds; #endif char peek[16]; /* Take a peek at the packet to get version and size information. */ #if HAVE_POLL readfd.fd = sockfd; readfd.events = POLLIN; readfd.revents = 0; xferbytes = poll(&readfd, 1, timeout ? timeout->tv_sec * 1000 + timeout->tv_usec / 1000 : -1); #else FD_ZERO(&readfds); FD_SET(sockfd, &readfds); xferbytes = select((int)sockfd + 1, &readfds, 0, 0, timeout); #endif if (xferbytes > 0) { if (socktype == SOCK_DGRAM) { socklen_t addrlen = sizeof(struct sockaddr_storage); xferbytes = recvfrom(sockfd, peek, 16, MSG_PEEK, peeraddr, &addrlen); } else xferbytes = recv(sockfd, peek, 16, MSG_PEEK); if (xferbytes <= 0) { #ifdef _WIN32 /* Winsock return WSAEMSGSIZE (even on MSG_PEEK!) if the amount of * data queued on the socket is larger than the specified buffer. * Just ignore it. */ if (WSAGetLastError() == WSAEMSGSIZE) { xferbytes = 16; } else #endif { errno = ENOTCONN; return -1; } } } else if (xferbytes == 0) { errno = ETIMEDOUT; return -1; } else { errno = ENOTCONN; return -1; } /* Now check the version and read the rest of the message. */ if (xferbytes >= 5 && (*peek == 1 || *peek == 2)) { /* Allocate the receive buffer as large as necessary. */ recvlen = PEEK_LENGTH(peek); *buf = SLPBufferRealloc(*buf, recvlen); if (*buf) { while ((*buf)->curpos < (*buf)->end) { #if HAVE_POLL readfd.fd = sockfd; readfd.events = POLLIN; readfd.revents = 0; xferbytes = poll(&readfd, 1, timeout ? timeout->tv_sec * 1000 + timeout->tv_usec / 1000 : -1); #else FD_SET(sockfd, &readfds); xferbytes = select((int)sockfd + 1, &readfds, 0, 0, timeout); #endif if (xferbytes > 0) { xferbytes = recv(sockfd, (char *)(*buf)->curpos, (int)((*buf)->end - (*buf)->curpos), 0); if (xferbytes > 0) (*buf)->curpos = (*buf)->curpos + xferbytes; else { errno = ENOTCONN; return -1; } } else if (xferbytes == 0) { errno = ETIMEDOUT; return -1; } else { errno = ENOTCONN; return -1; } } } else { errno = ENOMEM; return -1; } } else { errno = EINVAL; return -1; } return 0; }
/** Read inbound stream data. * * @param[in] socklist - The list of monitored sockets. * @param[in] sock - The socket to be read. * * @internal */ static void IncomingStreamRead(SLPList * socklist, SLPDSocket * sock) { int bytesread; size_t recvlen = 0; char peek[16]; socklen_t peeraddrlen = sizeof(struct sockaddr_storage); if (sock->state == STREAM_READ_FIRST) { /*---------------------------------------------------*/ /* take a peek at the packet to get size information */ /*---------------------------------------------------*/ bytesread = recvfrom(sock->fd, (char *)peek, 16, MSG_PEEK, (struct sockaddr *)&sock->peeraddr, &peeraddrlen); if (bytesread > 0 && bytesread >= (*peek == 2? 5: 4)) { recvlen = PEEK_LENGTH(peek); /* allocate the recvbuf big enough for the whole message */ sock->recvbuf = SLPBufferRealloc(sock->recvbuf, recvlen); if (sock->recvbuf) sock->state = STREAM_READ; else { SLPDLog("INTERNAL_ERROR - out of memory!\n"); sock->state = SOCKET_CLOSE; } } else { sock->state = SOCKET_CLOSE; return; } } if (sock->state == STREAM_READ) { /*------------------------------*/ /* recv the rest of the message */ /*------------------------------*/ bytesread = recv(sock->fd, (char *)sock->recvbuf->curpos, (int)(sock->recvbuf->end - sock->recvbuf->curpos), 0); if (bytesread > 0) { /* reset age to max because of activity */ sock->age = 0; sock->recvbuf->curpos += bytesread; if (sock->recvbuf->curpos == sock->recvbuf->end) { if (!sock->sendbuf) /* Some of the error handling code expects a sendbuf to be available * to be emptied, so make sure there is at least a minimal buffer */ sock->sendbuf = SLPBufferAlloc(1); switch (SLPDProcessMessage(&sock->peeraddr, &sock->localaddr, sock->recvbuf, &sock->sendbuf, 0)) { case SLP_ERROR_PARSE_ERROR: case SLP_ERROR_VER_NOT_SUPPORTED: case SLP_ERROR_MESSAGE_NOT_SUPPORTED: sock->state = SOCKET_CLOSE; break; default: if (!sock->sendbuf || sock->sendbuf->end == sock->sendbuf->start) { /* no answer available, just close socket */ sock->state = SOCKET_CLOSE; break; } /* some clients cannot cope with the OVERFLOW * bit set on a TCP stream, so always clear it */ if (sock->sendbuf->end - sock->sendbuf->start > 5) { if (sock->sendbuf->start[0] == 1) sock->sendbuf->start[4] &= ~SLPv1_FLAG_OVERFLOW; else sock->sendbuf->start[5] &= ~(SLP_FLAG_OVERFLOW >> 8); } sock->state = STREAM_WRITE_FIRST; IncomingStreamWrite(socklist, sock); } } }