/***************************************************************************/ /* */ /* Project: OpenSLP - OpenSource implementation of Service Location */ /* Protocol Version 2 */ /* */ /* File: slpd_socket.c */ /* */ /* Abstract: Socket specific functions implementation */ /* */ /* WARNING: NOT thread safe! */ /*-------------------------------------------------------------------------*/ /* */ /* 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_socket.h" #include "slpd_property.h" /*=========================================================================*/ /* common code includes */ /*=========================================================================*/ #include "slp_message.h" #include "slp_xmalloc.h" /*-------------------------------------------------------------------------*/ int EnableBroadcast(sockfd_t sockfd) /* Sets the socket options to receive broadcast traffic */ /* */ /* sockfd - the socket file descriptor to set option on */ /* */ /* returns - zero on success */ /*-------------------------------------------------------------------------*/ { #ifdef _WIN32 const char on = 1; #else const int on = 1; #endif return setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on)); } /*-------------------------------------------------------------------------*/ int SetMulticastTTL(sockfd_t sockfd, int ttl) /* Set the socket options for ttl */ /* */ /* sockfd - the socket file descriptor to set option on */ /* */ /* returns - zero on success */ /*-------------------------------------------------------------------------*/ { #if defined(linux) int optarg = ttl; #else /* Solaris and Tru64 expect a unsigned char parameter */ unsigned char optarg = (unsigned char)ttl; #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; 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 0; } /*-------------------------------------------------------------------------*/ int JoinSLPMulticastGroup(sockfd_t sockfd, struct in_addr* maddr, struct in_addr* addr) /* Sets the socket options to receive multicast traffic from the specified */ /* interface. */ /* */ /* sockfd - the socket file descriptor to set the options on. */ /* */ /* maddr - pointer to multicast group to join */ /* */ /* addr - pointer to address of the interface to join on */ /* */ /* returns - zero on success */ /*-------------------------------------------------------------------------*/ { struct ip_mreq mreq; /* join using the multicast address passed in */ memcpy(&mreq.imr_multiaddr, maddr, sizeof(struct in_addr)); /* join with specified interface */ memcpy(&mreq.imr_interface, addr, sizeof(struct in_addr)); return setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)); } /*-------------------------------------------------------------------------*/ int DropSLPMulticastGroup(sockfd_t sockfd, struct in_addr* maddr, struct in_addr* addr) /* Sets the socket options to not receive multicast traffic from the */ /* specified interface. */ /* */ /* sockfd - the socket file descriptor to set the options on. */ /* */ /* maddr - pointer to the multicast address */ /* */ /* addr - pointer to the multicast address */ /*-------------------------------------------------------------------------*/ { struct ip_mreq mreq; /* drop from the multicast group passed in */ memcpy(&mreq.imr_multiaddr, maddr, sizeof(struct in_addr)); /* drop for the specified interface */ memcpy(&mreq.imr_interface,addr,sizeof(addr)); return setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mreq,sizeof(mreq)); } /*-------------------------------------------------------------------------*/ int BindSocketToInetAddr(int sock, struct in_addr* addr) /* Binds the specified socket to the SLP port and interface. */ /* */ /* sock - the socket to be bound */ /* */ /* addr - the in_addr to bind to. */ /* */ /* Returns - zero on success, -1 on error. */ /*-------------------------------------------------------------------------*/ { int result; #ifdef _WIN32 char lowat; BOOL reuse = TRUE; #else int lowat; int reuse = 1; #endif struct sockaddr_in mysockaddr; memset(&mysockaddr, 0, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = htons(SLP_RESERVED_PORT); setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(const char *)&reuse,sizeof(reuse)); if(addr != NULL) { mysockaddr.sin_addr = *addr; } else { mysockaddr.sin_addr.s_addr = INADDR_ANY; } result = bind(sock, (struct sockaddr *) &mysockaddr,sizeof(mysockaddr)); if(result == 0) { /* set the receive and send buffer low water mark to 18 bytes (the length of the smallest slpv2 message) */ lowat = 18; setsockopt(sock,SOL_SOCKET,SO_RCVLOWAT,&lowat,sizeof(lowat)); setsockopt(sock,SOL_SOCKET,SO_SNDLOWAT,&lowat,sizeof(lowat)); } return result; } /*-------------------------------------------------------------------------*/ int BindSocketToLoopback(int sock) /* Binds the specified socket to the specified port of the loopback */ /* interface. */ /* */ /* sock - the socket to be bound */ /* */ /* Returns - zero on success, -1 on error. */ /*-------------------------------------------------------------------------*/ { struct in_addr loaddr; loaddr.s_addr = htonl(LOOPBACK_ADDRESS); return BindSocketToInetAddr(sock,&loaddr); } /*=========================================================================*/ SLPDSocket* SLPDSocketAlloc() /* Allocate memory for a new SLPDSocket. */ /* */ /* Returns: pointer to a newly allocated SLPDSocket, or NULL if out of */ /* memory. */ /*=========================================================================*/ { SLPDSocket* sock; sock = (SLPDSocket*)xmalloc(sizeof(SLPDSocket)); if(sock) { memset(sock,0,sizeof(SLPDSocket)); sock->fd = -1; } return sock; } /*=========================================================================*/ void SLPDSocketFree(SLPDSocket* sock) /* Frees memory associated with the specified SLPDSocket */ /* */ /* sock (IN) pointer to the socket to free */ /*=========================================================================*/ { /* close the socket descriptor */ CloseSocket(sock->fd); /* free receive buffer */ if(sock->recvbuf) { SLPBufferFree(sock->recvbuf); } /* free send buffer(s) */ if(sock->sendlist.count) { while(sock->sendlist.count) { SLPBufferFree((SLPBuffer)SLPListUnlink(&(sock->sendlist), sock->sendlist.head)); } } if(sock->sendbuf) { SLPBufferFree(sock->sendbuf); } /* free the actual socket structure */ xfree(sock); } /*==========================================================================*/ SLPDSocket* SLPDSocketCreateDatagram(struct in_addr* peeraddr, int type) /* myaddr - (IN) the address of the interface to join mcast on */ /* */ /* peeraddr - (IN) the address of the peer to connect to */ /* */ /* type (IN) DATAGRAM_UNICAST, DATAGRAM_MULTICAST, DATAGRAM_BROADCAST */ /* */ /* Returns: A datagram socket SLPDSocket->state will be set to */ /* DATAGRAM_UNICAST, DATAGRAM_MULTICAST, or DATAGRAM_BROADCAST */ /*==========================================================================*/ { SLPDSocket* sock; sock = SLPDSocketAlloc(); if(sock) { /* SLP_MAX_DATAGRAM_SIZE is as big as a datagram SLP */ /* can be. */ sock->recvbuf = SLPBufferAlloc(SLP_MAX_DATAGRAM_SIZE); sock->sendbuf = SLPBufferAlloc(SLP_MAX_DATAGRAM_SIZE); if(sock->recvbuf && sock->sendbuf) { sock->fd = socket(PF_INET, SOCK_DGRAM, 0); if(sock->fd >=0) { switch(type) { case DATAGRAM_BROADCAST: EnableBroadcast(sock->fd); break; case DATAGRAM_MULTICAST: SetMulticastTTL(sock->fd,G_SlpdProperty.multicastTTL); break; default: break; } sock->peeraddr.sin_family = AF_INET; sock->peeraddr.sin_addr = *peeraddr; sock->peeraddr.sin_port = htons(SLP_RESERVED_PORT); sock->state = type; } else { SLPDSocketFree(sock); sock = 0; } } else { SLPDSocketFree(sock); sock = 0; } } return sock; } /*==========================================================================*/ SLPDSocket* SLPDSocketCreateBoundDatagram(struct in_addr* myaddr, struct in_addr* peeraddr, int type) /* myaddr - (IN) the address of the interface to join mcast on */ /* */ /* peeraddr - (IN) the address of the peer to connect to */ /* */ /* type (IN) DATAGRAM_UNICAST, DATAGRAM_MULTICAST, DATAGRAM_BROADCAST */ /* */ /* Returns: A datagram socket SLPDSocket->state will be set to */ /* DATAGRAM_UNICAST, DATAGRAM_MULTICAST, or DATAGRAM_BROADCAST */ /*==========================================================================*/ { SLPDSocket* sock; struct in_addr* bindaddr; /*------------------------------------------*/ /* Adjust for multicast binding differences */ /*------------------------------------------*/ #ifdef LINUX bindaddr = peeraddr; #else if(type == DATAGRAM_MULTICAST) bindaddr = NULL; /* must bind to INADDR_ANY for multicast */ else bindaddr = peeraddr; #endif /*------------------------*/ /* Create and bind socket */ /*------------------------*/ sock = SLPDSocketAlloc(); if(sock) { sock->recvbuf = SLPBufferAlloc(SLP_MAX_DATAGRAM_SIZE); sock->sendbuf = SLPBufferAlloc(SLP_MAX_DATAGRAM_SIZE); sock->fd = socket(PF_INET, SOCK_DGRAM, 0); if(sock->fd >=0) { if(myaddr != NULL) sock->ifaddr.sin_addr = *myaddr; if(BindSocketToInetAddr(sock->fd, bindaddr) == 0) { if(peeraddr != NULL) { sock->peeraddr.sin_addr = *peeraddr; } switch(type) { case DATAGRAM_MULTICAST: if(JoinSLPMulticastGroup(sock->fd, peeraddr, myaddr) == 0) { sock->state = DATAGRAM_MULTICAST; goto SUCCESS; } break; case DATAGRAM_BROADCAST: if(EnableBroadcast(sock->fd) == 0) { sock->state = DATAGRAM_BROADCAST; goto SUCCESS; } break; case DATAGRAM_UNICAST: default: sock->state = DATAGRAM_UNICAST; goto SUCCESS; break; } } } } if(sock) { SLPDSocketFree(sock); } sock = 0; SUCCESS: return sock; } /*==========================================================================*/ SLPDSocket* SLPDSocketCreateListen(struct in_addr* peeraddr) /* */ /* peeraddr - (IN) the address of the peer to connect to */ /* */ /* type (IN) DATAGRAM_UNICAST, DATAGRAM_MULTICAST, DATAGRAM_BROADCAST */ /* */ /* Returns: A listening socket. SLPDSocket->state will be set to */ /* SOCKET_LISTEN. Returns NULL on error */ /*==========================================================================*/ { int fdflags; SLPDSocket* sock; sock = SLPDSocketAlloc(); if(sock) { sock->fd = socket(PF_INET, SOCK_STREAM, 0); if(sock->fd >= 0) { if(peeraddr != NULL) sock->ifaddr.sin_addr = *peeraddr; if(BindSocketToInetAddr(sock->fd, peeraddr) >= 0) { if(listen(sock->fd,5) == 0) { /* Set socket to non-blocking so subsequent calls to */ /* accept will *never* block */ #ifdef _WIN32 fdflags = 1; ioctlsocket(sock->fd, FIONBIO, &fdflags); #else fdflags = fcntl(sock->fd, F_GETFL, 0); fcntl(sock->fd,F_SETFL, fdflags | O_NONBLOCK); #endif sock->state = SOCKET_LISTEN; return sock; } } } } if(sock) { SLPDSocketFree(sock); } return 0; } /*==========================================================================*/ SLPDSocket* SLPDSocketCreateConnected(struct in_addr* addr) /* */ /* addr - (IN) the address of the peer to connect to */ /* */ /* Returns: A connected socket or a socket in the process of being connected*/ /* if the socket was connected the SLPDSocket->state will be set */ /* to writable. If the connect would block, SLPDSocket->state will*/ /* be set to connect. Return NULL on error */ /*==========================================================================*/ { #ifdef _WIN32 char lowat; u_long fdflags; #else int lowat; int fdflags; #endif SLPDSocket* sock = 0; sock = SLPDSocketAlloc(); if(sock == 0) { goto FAILURE; } /* create the stream socket */ sock->fd = socket(PF_INET,SOCK_STREAM,0); if(sock->fd < 0) { goto FAILURE; } /* set the socket to non-blocking */ #ifdef _WIN32 fdflags = 1; ioctlsocket(sock->fd, FIONBIO, &fdflags); #else fdflags = fcntl(sock->fd, F_GETFL, 0); fcntl(sock->fd,F_SETFL, fdflags | O_NONBLOCK); #endif /* zero then set peeraddr to connect to */ sock->peeraddr.sin_family = AF_INET; sock->peeraddr.sin_port = htons(SLP_RESERVED_PORT); sock->peeraddr.sin_addr = *addr; /* set the receive and send buffer low water mark to 18 bytes (the length of the smallest slpv2 message) */ lowat = 18; setsockopt(sock->fd,SOL_SOCKET,SO_RCVLOWAT,&lowat,sizeof(lowat)); setsockopt(sock->fd,SOL_SOCKET,SO_SNDLOWAT,&lowat,sizeof(lowat)); /* non-blocking connect */ if(connect(sock->fd, (struct sockaddr *) &(sock->peeraddr), sizeof(sock->peeraddr)) == 0) { /* Connection occured immediately */ sock->state = STREAM_CONNECT_IDLE; } else { #ifdef _WIN32 if(WSAEWOULDBLOCK == WSAGetLastError()) #else if(errno == EINPROGRESS) #endif { /* Connect would have blocked */ sock->state = STREAM_CONNECT_BLOCK; } else { goto FAILURE; } } return sock; /* cleanup on failure */ FAILURE: if(sock) { SLPDSocketFree(sock); sock = 0; } return sock; }