/** Receives datagram messages. * * Receives messages from one of the sockets in the specified * SLPXcastsSockets structure, @p sockets. * * @param[in] sockets - A pointer to the SOPXcastSockets structure that * describes the sockets from which to read messages. * @param[out] buf - A pointer to an SLPBuffer that will contain the * received message upon successful return. * @param[out] peeraddr - A pointer to a sockaddr structure that will * contain the address of the peer from which the message was received. * @param[in,out] timeout - A pointer to the timeval structure that * indicates how much time to wait for a message to arrive. * * @return Zero on success, or a non-zero with errno set on failure. */ int SLPXcastRecvMessage(const SLPXcastSockets * sockets, SLPBuffer * buf, void * peeraddr, struct timeval * timeout) { fd_set readfds; sockfd_t highfd; int i; int readable; int bytesread; int recvloop; char peek[16]; int result = 0; int mtu; mtu = SLPPropertyGetMTU(); /* recv loop */ recvloop = 1; while (recvloop) { /* Set the readfds */ FD_ZERO(&readfds); highfd = 0; for (i = 0; i < sockets->sock_count; i++) { FD_SET(sockets->sock[i],&readfds); if (sockets->sock[i] > highfd) highfd = sockets->sock[i]; } /* Select */ readable = select((int)(highfd + 1), &readfds, 0, 0, timeout); if (readable > 0) { /* Read the datagram */ for (i = 0; i < sockets->sock_count; i++) { if (FD_ISSET(sockets->sock[i], &readfds)) { /* Peek at the first 16 bytes of the header */ socklen_t peeraddrlen = sizeof(struct sockaddr_storage); bytesread = recvfrom(sockets->sock[i], peek, 16, MSG_PEEK, peeraddr, &peeraddrlen); if (bytesread == 16 #ifdef _WIN32 /* Win32 returns WSAEMSGSIZE if the message is larger * than the requested size, even with MSG_PEEK. But if * this is the error code we can be sure that the * message is at least 16 bytes */ || (bytesread == -1 && WSAGetLastError() == WSAEMSGSIZE) #endif ) { if (AS_UINT24(peek + 2) <= (unsigned int)mtu) { *buf = SLPBufferRealloc(*buf, AS_UINT24(peek + 2)); bytesread = recv(sockets->sock[i], (char *)(*buf)->curpos, (int)((*buf)->end - (*buf)->curpos), 0); /* This should never happen but we'll be paranoid*/ if (bytesread != (int)AS_UINT24(peek + 2)) (*buf)->end = (*buf)->curpos + bytesread; /* Message read. We're done! */ result = 0; recvloop = 0; break; } else { /* we got a bad message, or one that is too big! */ #ifndef UNICAST_NOT_SUPPORTED /* Reading mtu bytes on the socket */ *buf = SLPBufferRealloc(*buf, mtu); bytesread = recv(sockets->sock[i], (char *)(*buf)->curpos, (int)((*buf)->end - (*buf)->curpos), 0); /* This should never happen but we'll be paranoid*/ if (bytesread != mtu) (*buf)->end = (*buf)->curpos + bytesread; result = SLP_ERROR_RETRY_UNICAST; recvloop = 0; return result; #endif } } else { /* Not even 16 bytes available */ } } } } else if (readable == 0) { result = -1; errno = ETIMEDOUT; recvloop = 0; } else { result = -1; recvloop = 0; } } return result; }
/** Read an inbound datagram. * * @param[in] socklist - The list of monitored sockets. * @param[in] sock - The socket to be read. * * @internal */ static void IncomingDatagramRead(SLPList * socklist, SLPDSocket * sock) { int bytesread; int byteswritten; socklen_t peeraddrlen = sizeof(struct sockaddr_storage); char addr_str[INET6_ADDRSTRLEN]; sockfd_t sendsock = SLP_INVALID_SOCKET; (void)socklist; bytesread = recvfrom(sock->fd, (char*)sock->recvbuf->start, G_SlpdProperty.MTU, 0, (struct sockaddr *)&sock->peeraddr, &peeraddrlen); if (bytesread > 0) { sock->recvbuf->end = sock->recvbuf->start + bytesread; 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: break; default: #ifdef DARWIN /* If the socket is a multicast socket, find the designated UDP output socket for sending */ if (sock->state == DATAGRAM_MULTICAST) if ((sendsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != SLP_INVALID_SOCKET) SLPNetworkSetSndRcvBuf(sendsock); #endif if (sendsock == SLP_INVALID_SOCKET) sendsock = sock->fd; /* check to see if we should send anything, breaking up individual packets in the buffer into different sendto calls (should only be an issue with the loopback DA response)*/ if (sock->sendbuf) { sock->sendbuf->curpos = sock->sendbuf->start; while (sock->sendbuf->curpos < sock->sendbuf->end) { int packetbytes = AS_UINT24(sock->sendbuf->curpos + 2); byteswritten = sendto(sendsock, (char*)sock->sendbuf->curpos, packetbytes, 0, (struct sockaddr *)&sock->peeraddr, SLPNetAddrLen(&sock->peeraddr)); if (byteswritten != packetbytes) { /* May be an overflow reply */ int flags = AS_UINT16(sock->sendbuf->curpos + 5); if ((byteswritten == -1) && #ifdef _WIN32 (WSAGetLastError() == WSAEMSGSIZE) && #else (errno == EMSGSIZE) && #endif (flags & SLP_FLAG_OVERFLOW)) { int byteswrittenmax = sendto(sendsock, (char*)sock->sendbuf->curpos, G_SlpdProperty.MTU, 0, (struct sockaddr *)&sock->peeraddr, SLPNetAddrLen(&sock->peeraddr)); if (byteswrittenmax == G_SlpdProperty.MTU) byteswritten = packetbytes; } } if (byteswritten != packetbytes) SLPDLog("NETWORK_ERROR - %d replying %s\n", errno, SLPNetSockAddrStorageToString(&(sock->peeraddr), addr_str, sizeof(addr_str))); sock->sendbuf->curpos += packetbytes; } /* Only close if we allocated a new socket */ if (sendsock != sock->fd) closesocket(sendsock); } break; } } }