/***************************************************************************/ /* */ /* Project: OpenSLP - OpenSource implementation of Service Location */ /* Protocol Version 2 */ /* */ /* File: slpd_incoming.c */ /* */ /* Abstract: Handles "incoming" network conversations requests made by */ /* other agents to slpd. (slpd_outgoing.c handles reqests */ /* made by slpd to other agents) */ /* */ /*-------------------------------------------------------------------------*/ /* */ /* Please submit patches to http://www.openslp.org */ /* */ /*-------------------------------------------------------------------------*/ /* */ /* Copyright (C) 2000 Caldera Systems, Inc */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or without */ /* modification, are permitted provided that the following conditions are */ /* met: */ /* */ /* Redistributions of source code must retain the above copyright */ /* notice, this list of conditions and the following disclaimer. */ /* */ /* Redistributions in binary form must reproduce the above copyright */ /* notice, this list of conditions and the following disclaimer in */ /* the documentation and/or other materials provided with the */ /* distribution. */ /* */ /* Neither the name of Caldera Systems nor the names of its */ /* contributors may be used to endorse or promote products derived */ /* from this software without specific prior written permission. */ /* */ /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ /* `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */ /* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA */ /* SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ /* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */ /* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */ /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ /* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* */ /***************************************************************************/ /*=========================================================================*/ /* slpd includes */ /*=========================================================================*/ #include "slpd_incoming.h" #include "slpd_socket.h" #include "slpd_process.h" #include "slpd_property.h" #include "slpd_log.h" /*=========================================================================*/ /* common code includes */ /*=========================================================================*/ #include "slp_xmalloc.h" #include "slp_message.h" /*=========================================================================*/ SLPList G_IncomingSocketList = {0,0,0}; /*=========================================================================*/ /*-------------------------------------------------------------------------*/ void IncomingDatagramRead(SLPList* socklist, SLPDSocket* sock) /*-------------------------------------------------------------------------*/ { int bytesread; int bytestowrite; int byteswritten; int peeraddrlen = sizeof(struct sockaddr_in); bytesread = recvfrom(sock->fd, sock->recvbuf->start, SLP_MAX_DATAGRAM_SIZE, 0, (struct sockaddr *) &(sock->peeraddr), &peeraddrlen); if (bytesread > 0) { sock->recvbuf->end = sock->recvbuf->start + bytesread; switch (SLPDProcessMessage(&sock->peeraddr, sock->recvbuf, &(sock->sendbuf))) { case SLP_ERROR_PARSE_ERROR: case SLP_ERROR_VER_NOT_SUPPORTED: case SLP_ERROR_MESSAGE_NOT_SUPPORTED: break; default: /* check to see if we should send anything */ bytestowrite = sock->sendbuf->end - sock->sendbuf->start; if (bytestowrite > 0) { byteswritten = sendto(sock->fd, sock->sendbuf->start, bytestowrite, 0, (struct sockaddr *)&(sock->peeraddr), sizeof(struct sockaddr_in)); if (byteswritten != bytestowrite) { SLPDLog("NETWORK_ERROR - %d replying %s\n", errno, inet_ntoa(sock->peeraddr.sin_addr)); } } } } } /*-------------------------------------------------------------------------*/ void IncomingStreamWrite(SLPList* socklist, SLPDSocket* sock) /*-------------------------------------------------------------------------*/ { int byteswritten, flags = 0; #if defined(MSG_DONTWAIT) flags = MSG_DONTWAIT; #endif if (sock->state == STREAM_WRITE_FIRST) { /* make sure that the start and curpos pointers are the same */ sock->sendbuf->curpos = sock->sendbuf->start; sock->state = STREAM_WRITE; } if (sock->sendbuf->end - sock->sendbuf->start != 0) { byteswritten = send(sock->fd, sock->sendbuf->curpos, sock->sendbuf->end - sock->sendbuf->start, flags); if (byteswritten > 0) { /* reset lifetime to max because of activity */ sock->age = 0; sock->sendbuf->curpos += byteswritten; if (sock->sendbuf->curpos == sock->sendbuf->end) { /* message is completely sent */ sock->state = STREAM_READ_FIRST; } } else { #ifdef _WIN32 if (WSAEWOULDBLOCK == WSAGetLastError()) #else if (errno == EWOULDBLOCK) #endif { /* Error occured or connection was closed */ sock->state = SOCKET_CLOSE; } } } } /*-------------------------------------------------------------------------*/ void IncomingStreamRead(SLPList* socklist, SLPDSocket* sock) /*-------------------------------------------------------------------------*/ { int bytesread, recvlen = 0; char peek[16]; int peeraddrlen = sizeof(struct sockaddr_in); 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 && bytesread >= (*peek == 2 ? 5 : 4)) { if (*peek == 2) recvlen = AsUINT24(peek + 2); else if (*peek == 1) /* SLPv1 packet */ recvlen = AsUINT16(peek + 2); /* one byte is minimum */ if (recvlen <= 0) recvlen = 1; /* 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, sock->recvbuf->curpos, 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) { switch (SLPDProcessMessage(&sock->peeraddr, sock->recvbuf, &(sock->sendbuf))) { case SLP_ERROR_PARSE_ERROR: case SLP_ERROR_VER_NOT_SUPPORTED: case SLP_ERROR_MESSAGE_NOT_SUPPORTED: sock->state = SOCKET_CLOSE; break; default: sock->state = STREAM_WRITE_FIRST; IncomingStreamWrite(socklist, sock); } } } else { /* error in recv() or eof */ sock->state = SOCKET_CLOSE; } } } /*-------------------------------------------------------------------------*/ void IncomingSocketListen(SLPList* socklist, SLPDSocket* sock) /*-------------------------------------------------------------------------*/ { int fdflags; sockfd_t fd; SLPDSocket* connsock; struct sockaddr_in peeraddr; socklen_t peeraddrlen; #ifdef _WIN32 const char lowat = SLPD_SMALLEST_MESSAGE; #else const int lowat = SLPD_SMALLEST_MESSAGE; #endif /* Only accept if we can. If we still maximum number of sockets, just*/ /* ignore the connection */ if (socklist->count < SLPD_MAX_SOCKETS) { peeraddrlen = sizeof(peeraddr); fd = accept(sock->fd, (struct sockaddr *) &peeraddr, &peeraddrlen); if (fd >= 0) { connsock = SLPDSocketAlloc(); if (connsock) { /* setup the accepted socket */ connsock->fd = fd; connsock->peeraddr = peeraddr; connsock->state = STREAM_READ_FIRST; /* Set the low water mark on the accepted socket */ setsockopt(connsock->fd,SOL_SOCKET,SO_RCVLOWAT,&lowat,sizeof(lowat)); setsockopt(connsock->fd,SOL_SOCKET,SO_SNDLOWAT,&lowat,sizeof(lowat)); /* set accepted socket to non blocking */ #ifdef _WIN32 fdflags = 1; ioctlsocket(connsock->fd, FIONBIO, &fdflags); #else fdflags = fcntl(connsock->fd, F_GETFL, 0); fcntl(connsock->fd,F_SETFL, fdflags | O_NONBLOCK); #endif SLPListLinkHead(socklist,(SLPListItem*)connsock); } } } } /*=========================================================================*/ void SLPDIncomingHandler(int* fdcount, fd_set* readfds, fd_set* writefds) /* Handles all outgoing requests that are pending on the specified file */ /* discriptors */ /* */ /* fdcount (IN/OUT) number of file descriptors marked in fd_sets */ /* */ /* readfds (IN) file descriptors with pending read IO */ /* */ /* writefds (IN) file descriptors with pending read IO */ /*=========================================================================*/ { SLPDSocket* sock; sock = (SLPDSocket*) G_IncomingSocketList.head; while (sock && *fdcount) { if (FD_ISSET(sock->fd,readfds)) { switch (sock->state) { case SOCKET_LISTEN: IncomingSocketListen(&G_IncomingSocketList,sock); break; case DATAGRAM_UNICAST: case DATAGRAM_MULTICAST: case DATAGRAM_BROADCAST: IncomingDatagramRead(&G_IncomingSocketList,sock); break; case STREAM_READ: case STREAM_READ_FIRST: IncomingStreamRead(&G_IncomingSocketList,sock); break; default: break; } *fdcount = *fdcount - 1; } else if (FD_ISSET(sock->fd,writefds)) { switch (sock->state) { case STREAM_WRITE: case STREAM_WRITE_FIRST: IncomingStreamWrite(&G_IncomingSocketList,sock); break; default: break; } *fdcount = *fdcount - 1; } sock = (SLPDSocket*)sock->listitem.next; } } /*=========================================================================*/ void SLPDIncomingAge(time_t seconds) /*=========================================================================*/ { SLPDSocket* del = 0; SLPDSocket* sock = (SLPDSocket*)G_IncomingSocketList.head; while (sock) { switch (sock->state) { case STREAM_READ_FIRST: case STREAM_READ: case STREAM_WRITE_FIRST: case STREAM_WRITE: if (G_IncomingSocketList.count > SLPD_COMFORT_SOCKETS) { /* Accellerate ageing cause we are low on sockets */ if (sock->age > SLPD_CONFIG_BUSY_CLOSE_CONN) { del = sock; } } else { if (sock->age > SLPD_CONFIG_CLOSE_CONN) { del = sock; } } sock->age = sock->age + seconds; break; default: /* don't age the other sockets at all */ break; } sock = (SLPDSocket*)sock->listitem.next; if (del) { SLPDSocketFree((SLPDSocket*)SLPListUnlink(&G_IncomingSocketList,(SLPListItem*)del)); del = 0; } } } /*=========================================================================*/ int SLPDIncomingInit() /* Initialize incoming socket list to have appropriate sockets for all */ /* network interfaces */ /* */ /* Returns Zero on success non-zero on error */ /*=========================================================================*/ { char* begin = NULL; char* beginSave = NULL; char* end = NULL; int finished; struct in_addr myaddr; struct in_addr mcastaddr; struct in_addr bcastaddr; struct in_addr loaddr; SLPDSocket* sock; /*------------------------------------------------------------*/ /* First, remove all of the sockets that might be in the list */ /*------------------------------------------------------------*/ while (G_IncomingSocketList.count) { SLPDSocketFree((SLPDSocket*)SLPListUnlink(&G_IncomingSocketList,G_IncomingSocketList.head)); } /*--------------------------------------------------*/ /* set up address to use for loopback and broadcast */ /*--------------------------------------------------*/ loaddr.s_addr = htonl(LOOPBACK_ADDRESS); bcastaddr.s_addr = htonl(SLP_BCAST_ADDRESS); mcastaddr.s_addr = htonl(SLP_MCAST_ADDRESS); /*--------------------------------------------------------------------*/ /* Create SOCKET_LISTEN socket for LOOPBACK for the library to talk to*/ /*--------------------------------------------------------------------*/ sock = SLPDSocketCreateListen(&loaddr); if (sock) { SLPListLinkTail(&G_IncomingSocketList,(SLPListItem*)sock); SLPDLog("Listening on loopback...\n"); } else { SLPDLog("NETWORK_ERROR - Could not listen on loopback\n"); SLPDLog("INTERNAL_ERROR - No SLPLIB support will be available\n"); } /*---------------------------------------------------------------------*/ /* Create sockets for all of the interfaces in the interfaces property */ /*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/ /* Copy G_SlpdProperty.interfaces to a temporary buffer to parse the */ /* string in a safety way */ /*---------------------------------------------------------------------*/ if (G_SlpdProperty.interfaces != NULL) { begin = xstrdup((char *) G_SlpdProperty.interfaces); beginSave = begin; /* save pointer for free() operation later */ end = begin; finished = 0; } else { finished = 1; /* if no interface is defined, */ /* don't even enter the parsing loop */ } for (; (finished == 0); begin = ++end) { while (*end && *end != ',') end ++; if (*end == 0) finished = 1; *end = 0; /* Terminate string. */ if (end <= begin) continue; /* Skip empty entries */ /* begin now points to a null terminated ip address string */ myaddr.s_addr = inet_addr(begin); /*------------------------------------------------*/ /* Create TCP_LISTEN that will handle unicast TCP */ /*------------------------------------------------*/ sock = SLPDSocketCreateListen(&myaddr); if (sock) { SLPListLinkTail(&G_IncomingSocketList,(SLPListItem*)sock); SLPDLog("Listening on %s ...\n",inet_ntoa(myaddr)); } /*----------------------------------------------------------------*/ /* Create socket that will handle multicast UDP. */ /*----------------------------------------------------------------*/ sock = SLPDSocketCreateBoundDatagram(&myaddr, &mcastaddr, DATAGRAM_MULTICAST); if (sock) { SLPListLinkTail(&G_IncomingSocketList,(SLPListItem*)sock); SLPDLog("Multicast socket on %s ready\n",inet_ntoa(myaddr)); } else { SLPDLog("Couldn't bind to multicast for interface %s (%s)\n", inet_ntoa(myaddr), strerror(errno)); } #if defined(ENABLE_SLPv1) if (G_SlpdProperty.isDA) { /*------------------------------------------------------------*/ /* Create socket that will handle multicast UDP for SLPv1 DA */ /* Discovery. */ /*------------------------------------------------------------*/ mcastaddr.s_addr = htonl(SLPv1_DA_MCAST_ADDRESS); sock = SLPDSocketCreateBoundDatagram(&myaddr, &mcastaddr, DATAGRAM_MULTICAST); if (sock) { SLPListLinkTail(&G_IncomingSocketList,(SLPListItem*)sock); SLPDLog("SLPv1 DA Discovery Multicast socket on %s ready\n", inet_ntoa(myaddr)); } } #endif /*--------------------------------------------*/ /* Create socket that will handle unicast UDP */ /*--------------------------------------------*/ sock = SLPDSocketCreateBoundDatagram(&myaddr, &myaddr, DATAGRAM_UNICAST); if (sock) { SLPListLinkTail(&G_IncomingSocketList,(SLPListItem*)sock); SLPDLog("Unicast socket on %s ready\n",inet_ntoa(myaddr)); } } if (beginSave) xfree(beginSave); /*--------------------------------------------------------*/ /* Create socket that will handle broadcast UDP */ /*--------------------------------------------------------*/ sock = SLPDSocketCreateBoundDatagram(&myaddr, &bcastaddr, DATAGRAM_BROADCAST); if (sock) { SLPListLinkTail(&G_IncomingSocketList,(SLPListItem*)sock); SLPDLog("Broadcast socket for %s ready\n", inet_ntoa(bcastaddr)); } if (G_IncomingSocketList.count == 0) { SLPDLog("No usable interfaces\n"); return 1; } return 0; } /*=========================================================================*/ int SLPDIncomingDeinit() /* Deinitialize incoming socket list to have appropriate sockets for all */ /* network interfaces */ /* */ /* Returns Zero on success non-zero on error */ /*=========================================================================*/ { SLPDSocket* del = 0; SLPDSocket* sock = (SLPDSocket*)G_IncomingSocketList.head; while (sock) { del = sock; sock = (SLPDSocket*)sock->listitem.next; if (del) { SLPDSocketFree((SLPDSocket*)SLPListUnlink(&G_IncomingSocketList,(SLPListItem*)del)); del = 0; } } return 0; } #ifdef DEBUG /*=========================================================================*/ void SLPDIncomingSocketDump() /*=========================================================================*/ { } #endif