/***************************************************************************/ /* */ /* Project: OpenSLP - OpenSource implementation of Service Location */ /* Protocol */ /* */ /* File: slp_network.c */ /* */ /* Abstract: Implementation for functions that are related */ /* network (and ipc) communication. */ /* */ /*-------------------------------------------------------------------------*/ /* */ /* 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. */ /* */ /***************************************************************************/ #include "slp_network.h" /*=========================================================================*/ int SLPNetworkConnectStream(struct sockaddr_in* peeraddr, struct timeval* timeout) /* Connect a TCP stream to the specified peer */ /* */ /* peeraddr (IN) pointer to the peer to connect to */ /* */ /* timeout (IN) pointer to the maximum time to spend connecting */ /* */ /* returns: a connected socket or -1 */ /*=========================================================================*/ { #ifdef _WIN32 char lowat; #else int lowat; #endif int result; /* TODO: Make this connect non-blocking so that it will timeout */ result = socket(AF_INET,SOCK_STREAM,0); if(result >= 0) { if(connect(result, (struct sockaddr*)peeraddr, sizeof(struct sockaddr_in)) == 0) { /* set the receive and send buffer low water mark to 18 bytes (the length of the smallest slpv2 message) */ lowat = 18; setsockopt(result,SOL_SOCKET,SO_RCVLOWAT,&lowat,sizeof(lowat)); setsockopt(result,SOL_SOCKET,SO_SNDLOWAT,&lowat,sizeof(lowat)); return result;; } else { close(result); result = -1; } } return result; } /*=========================================================================*/ int SLPNetworkConnectToMulticast(struct sockaddr_in* peeraddr, int ttl) /* Creates a socket and provides a peeraddr to send to */ /* */ /* peeraddr (OUT) pointer to receive the connected DA's address */ /* */ /* ttl (IN) ttl for the mcast socket */ /* */ /* Returns Valid socket or -1 if no DA connection can be made */ /*=========================================================================*/ { int sockfd; #if defined(linux) int optarg; #else /* Solaris and Tru64 expect a unsigned char parameter */ unsigned char optarg; #endif #ifdef _WIN32 BOOL Reuse = TRUE; int TTLArg; struct sockaddr_in mysockaddr; memset(&mysockaddr, 0, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = 0; #endif /* setup multicast socket */ sockfd = socket(AF_INET,SOCK_DGRAM,0); if(sockfd >= 0) { peeraddr->sin_family = AF_INET; peeraddr->sin_port = htons(SLP_RESERVED_PORT); peeraddr->sin_addr.s_addr = htonl(SLP_MCAST_ADDRESS); optarg = ttl; #ifdef _WIN32 TTLArg = ttl; if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&Reuse, sizeof(Reuse)) || bind(sockfd, (struct sockaddr *)&mysockaddr, sizeof(mysockaddr)) || setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&TTLArg, sizeof(TTLArg))) { return -1; } #else if(setsockopt(sockfd,IPPROTO_IP,IP_MULTICAST_TTL,&optarg,sizeof(optarg))) { return -1; } #endif } return sockfd; } /*=========================================================================*/ int SLPNetworkConnectToBroadcast(struct sockaddr_in* peeraddr) /* Creates a socket and provides a peeraddr to send to */ /* */ /* peeraddr (OUT) pointer to receive the connected DA's address */ /* */ /* Returns Valid socket or -1 if no DA connection can be made */ /*=========================================================================*/ { int sockfd; #ifdef _WIN32 BOOL on = 1; #else int on = 1; #endif /* setup broadcast */ sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd >= 0) { peeraddr->sin_family = AF_INET; peeraddr->sin_port = htons(SLP_RESERVED_PORT); peeraddr->sin_addr.s_addr = htonl(SLP_BCAST_ADDRESS); if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on))) { return -1; } } return sockfd; } /*=========================================================================*/ int SLPNetworkSendMessage(int sockfd, int socktype, SLPBuffer buf, struct sockaddr_in* peeraddr, struct timeval* timeout) /* Sends a message */ /* */ /* Returns - zero on success non-zero on failure */ /* */ /* errno EPIPE error during write */ /* ETIME read timed out */ /*=========================================================================*/ { fd_set writefds; int xferbytes; int flags = 0; #if defined(MSG_NOSIGNAL) flags = MSG_NOSIGNAL; #endif buf->curpos = buf->start; while(buf->curpos < buf->end) { FD_ZERO(&writefds); FD_SET(sockfd, &writefds); xferbytes = select(sockfd + 1, 0, &writefds, 0, timeout); if(xferbytes > 0) { if(socktype == SOCK_DGRAM) { xferbytes = sendto(sockfd, buf->curpos, buf->end - buf->curpos, flags, (struct sockaddr *)peeraddr, sizeof(struct sockaddr_in)); } else { xferbytes = send(sockfd, buf->curpos, buf->end - buf->curpos, flags); } if(xferbytes > 0) { buf->curpos = buf->curpos + xferbytes; } else { errno = EPIPE; return -1; } } else if(xferbytes == 0) { /* timed out */ errno = ETIMEDOUT; return -1; } else { errno = EPIPE; return -1; } } return 0; } /*=========================================================================*/ int SLPNetworkRecvMessage(int sockfd, int socktype, SLPBuffer* buf, struct sockaddr_in* peeraddr, struct timeval* timeout) /* Receives a message */ /* */ /* Returns - zero on success, non-zero on failure */ /* */ /* errno ENOTCONN error during read */ /* ETIME read timed out */ /* ENOMEM out of memory */ /* EINVAL parse error */ /*=========================================================================*/ { int xferbytes, recvlen; fd_set readfds; char peek[16]; int peeraddrlen = sizeof(struct sockaddr_in); /*---------------------------------------------------------------*/ /* take a peek at the packet to get version and size information */ /*---------------------------------------------------------------*/ FD_ZERO(&readfds); FD_SET(sockfd, &readfds); xferbytes = select(sockfd + 1, &readfds, 0 , 0, timeout); if(xferbytes > 0) { if(socktype == SOCK_DGRAM) { xferbytes = recvfrom(sockfd, peek, 16, MSG_PEEK, (struct sockaddr *)peeraddr, &peeraddrlen); } else { xferbytes = recv(sockfd, peek, 16, MSG_PEEK); } if(xferbytes <= 0) { #ifdef _WIN32 if(WSAGetLastError() != WSAEMSGSIZE) { errno = ENOTCONN; return -1; } #else errno = ENOTCONN; return -1; #endif } } else if(xferbytes == 0) { errno = ETIMEDOUT; return -1; } else { errno = ENOTCONN; return -1; } /*------------------------------*/ /* Read the rest of the message */ /*------------------------------*/ /* check the version */ if(xferbytes >= 5 && *peek == 2) { /* allocate the recvmsg big enough for the whole message */ recvlen = AsUINT24(peek + 2); /* one byte is minimum */ if (recvlen <= 0) recvlen = 1; *buf = SLPBufferRealloc(*buf, recvlen); if(*buf) { while((*buf)->curpos < (*buf)->end) { FD_ZERO(&readfds); FD_SET(sockfd, &readfds); xferbytes = select(sockfd + 1, &readfds, 0 , 0, timeout); if(xferbytes > 0) { xferbytes = recv(sockfd, (*buf)->curpos, (*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; } } /* end of main read while. */ } else { errno = ENOMEM; return -1; } } else { errno = EINVAL; return -1; } return 0; }