You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

607 lines
23 KiB

/***************************************************************************/
/* */
/* 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;
}