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.

419 lines
15 KiB

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