#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h> // for _POSIX_THREADS
#include <pthread.h> // for pthread_*_t
#include "mslp_sd.h"
#include "slp.h"
#include "mslp.h"
#include "mslp_dat.h"
#include "mslplib.h"
static SLPBoolean time_remaining(long lTimeStart, time_t tMaxwait,
long *plWaits, int *piSend, int iNumSent, struct timeval *ptv);
SLPInternalError mslplib_init_network(SOCKET *psdUDP, SOCKET *psdTCP, SOCKET *psdMax)
{
int iErr;
unsigned char ttl = 255;
u_char loop = 1; char* endPtr = NULL;
if ( SLPGetProperty("net.slp.multicastTTL") )
ttl = (unsigned char) strtol(SLPGetProperty("net.slp.multicastTTL"),&endPtr,10);
if (OPEN_NETWORKING() < 0) return SLP_NETWORK_INIT_FAILED;
*psdUDP = socket(AF_INET, SOCK_DGRAM, 0);
*psdTCP = socket(AF_INET, SOCK_STREAM, 0);
if (*psdUDP == INVALID_SOCKET || *psdTCP == INVALID_SOCKET)
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "Could not allocate a socket" );
#endif
return SLP_NETWORK_INIT_FAILED;
}
iErr = setsockopt(*psdUDP, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl));
#ifdef ENABLE_SLP_LOGGING
if (iErr < 0)
mslplog(SLP_LOG_DEBUG,"mslplib_init_network: Could not set multicast TTL",strerror(errno));
#endif
iErr = setsockopt( *psdUDP, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop) );
#ifdef ENABLE_SLP_LOGGING
if (iErr < 0)
{
mslplog(SLP_LOG_DEBUG,"mslplib_init_network: Could not set ip multicast loopback option",strerror(errno));
}
#endif
if (*psdUDP > *psdTCP) {
*psdMax = *psdUDP;
} else {
*psdMax = *psdTCP;
}
return SLP_OK;
}
void mslplib_daadvert_callback(SLPHandle hSLP,
int iErrCode,
struct sockaddr_in sin,
const char *pcScopeList,
const char *pcAttrList,
long lBootTime,
void *pvUser) {
DATable *pdat = (DATable *) pvUser;
int iErr;
hSLP = hSLP;
iErrCode = iErrCode;
pcAttrList = pcAttrList;
if ((iErr=dat_daadvert_in(pdat, sin, pcScopeList, lBootTime)) < 0) {
#ifdef ENABLE_SLP_LOGGING
SLPInternalError slperr = (SLPInternalError) iErr;
mslplog(SLP_LOG_DA,"mslplib_daadvert_callback: dat_daadvert_in failed",
slperror(slperr));
#endif
}
}
SLPInternalError generate_srvrqst(char *pcSendBuf, int *piSendSz,
const char *pcLangTag,
const char *pcScope, const char *pcSrvType, const char *pcFilter) {
int offset = 0;
SLPInternalError err;
int iMTU = *piSendSz;
(void) memset(pcSendBuf, 0, *piSendSz);
if ((err = add_header(pcLangTag,pcSendBuf,iMTU,SRVRQST,0,&offset))
!= SLP_OK) {
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR,"generate_srvrqst: could not add header",err);
}
offset += 2;
if ((err = add_string(pcSendBuf,iMTU,pcSrvType,&offset)) != SLP_OK) {
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR,"generate_srvrqst: could not add srvtype",err);
}
if ((err = add_string(pcSendBuf,iMTU,pcScope,&offset)) != SLP_OK) {
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR,"generate_srvrqst: could not add scope",err);
}
if ((err = add_string(pcSendBuf,iMTU,pcFilter,&offset)) != SLP_OK) {
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR,"generate_srvrqst: could not add filter",err);
}
if ((err = add_sht(pcSendBuf, iMTU, 0, &offset)) != SLP_OK) {
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_ERR,"generate_srvrqst: could not add 0 SLP SPI len", err);
}
SETLEN(pcSendBuf,offset);
*piSendSz = offset;
return SLP_OK;
}
#ifdef SLPTCP
SLPInternalError get_tcp_result(const char *pcSendBuf, int iSendSz, struct sockaddr_in sin, char **ppcInBuf, int *piInSz)
{
SOCKET sd;
int iResult;
char pcHeader[HDRLEN];
if ((sd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
#ifdef ENABLE_SLP_LOGGING
mslplog(SLP_LOG_DROP,"get_tcp_result could not init tcp socket",strerror(errno));
#endif
return SLP_NETWORK_ERROR;
}
if (connect(sd,(struct sockaddr*) &sin, sizeof(sin)) < 0)
{
CLOSESOCKET(sd);
#ifdef ENABLE_SLP_LOGGING
mslplog(SLP_LOG_DROP,"get_tcp_result could not connect to addr",strerror(errno));
#endif
return SLP_NETWORK_ERROR;
}
iResult = writen(sd,(void*)pcSendBuf,iSendSz);
if (iResult < 0)
{
CLOSESOCKET(sd);
#ifdef ENABLE_SLP_LOGGING
mslplog(SLP_LOG_DROP,"get_tcp_result could not writen",strerror(errno));
#endif
return SLP_NETWORK_ERROR;
}
iResult = readn(sd,pcHeader,HDRLEN);
if (iResult < HDRLEN)
{
CLOSESOCKET(sd);
#ifdef ENABLE_SLP_LOGGING
mslplog(SLP_LOG_DROP,"get_tcp_result could not readn header",strerror(errno));
#endif
return SLP_NETWORK_ERROR;
}
*piInSz = GETLEN(pcHeader);
*ppcInBuf = safe_malloc(*piInSz,pcHeader,HDRLEN);
if( !*ppcInBuf ) return SLP_NETWORK_ERROR;
if ((iResult = readn(sd,&(*ppcInBuf)[HDRLEN],(*piInSz)-HDRLEN)) < 0)
{
SLPFree(*ppcInBuf);
CLOSESOCKET(sd);
#ifdef ENABLE_SLP_LOGGING
mslplog(SLP_LOG_DROP,"get_tcp_result could not readn message",strerror(errno));
#endif
return SLP_NETWORK_ERROR;
}
CLOSESOCKET(sd);
return SLP_OK;
}
#endif
SLPInternalError get_unicast_result(
time_t timeout,
SOCKET sdSend,
char *pcSendBuf,
int iSendSz,
char *pcRecvBuf,
int iRecvSz,
int *piInSz,
struct sockaddr_in sin)
{
struct sockaddr_in insin;
int iInSinSz = sizeof insin;
time_t tStart = time(0);
time_t tElapsed;
int iErr;
int loop = 0;
fd_set fds, allset;
struct timeval tv = { 2, 0 };
*piInSz = 0;
FD_ZERO(&allset);
FD_SET(sdSend,&allset);
for (loop = 0; loop < 3; loop++)
{
tElapsed = time(0) - tStart;
if (loop > 0 && (tElapsed < (time_t) (loop*3)) && tElapsed < timeout)
{
struct timeval tv2 = {3,0};
(void) select(0,NULL,NULL,NULL,&tv2);
}
if ((iErr = sendto(sdSend, pcSendBuf, iSendSz, 0, (struct sockaddr*) &sin,sizeof sin)) < 0)
{
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP, "get_unicast_result sendto",SLP_NETWORK_ERROR);
}
fds = allset;
iErr = select(sdSend+1,&fds,NULL,NULL,&tv);
if (iErr < 0)
{
if ( errno == EINTR )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DROP, "get_converge_result: select received EINTR");
#endif
continue;
}
else
LOG_STD_ERROR_AND_RETURN(SLP_LOG_ERR,"get_converge_result: select",SLP_INTERNAL_SYSTEM_ERROR);
}
else if (iErr == 0)
{
continue;
}
else
{
if ((iErr = recvfrom(sdSend, pcRecvBuf, iRecvSz, 0, (struct sockaddr*)&insin, &iInSinSz)) < 0)
{
if ( errno == EINTR )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DROP, "get_unicast_result recvfrom received EINTR");
#endif
continue;
}
else
LOG_SLP_ERROR_AND_RETURN(SLP_LOG_DROP, "get_unicast_result recvfrom", SLP_NETWORK_ERROR);
}
*piInSz = iErr;
return SLP_OK;
}
}
return SLP_NETWORK_TIMED_OUT;
}
#define NUM_SECS_BETWEEN_REQUESTS 5
#define INTERVALS 15
pthread_mutex_t gMulticastTTLLock = PTHREAD_MUTEX_INITIALIZER;;
SLPInternalError get_converge_result(
time_t tMaxwait,
SOCKET sd,
char *pcSendBuf,
int iSendSz,
char *pcRecvBuf,
int iRecvSz,
struct sockaddr_in sin,
unsigned char ttl,
void *pvUser,
SLPHandle hSLP,
void *pvCallback,
CBType cbt)
{
int iLast = 0;
int loop = 0;
int sendit=0,numsent=0;
int result;
int len;
SLPInternalError err = SLP_OK;
struct sockaddr_in insin;
long lTimeStart = SDGetTime();
struct timeval tv;
fd_set fds, allset;
int numIntervals;
long plWaits[INTERVALS]; #ifdef USE_PR_LIST
char* pcPRList = NULL;
char* pcPrevPRList = NULL;
#endif
int iErr;
div_t divResult;
long tMaxWaitSecs = tMaxwait/1000;
char errbuf[120];
char* endPtr = NULL;
bool prListMaxedOut = false;
divResult = div(tMaxwait,NUM_SECS_BETWEEN_REQUESTS*1000);
if ( divResult.quot > INTERVALS )
numIntervals = INTERVALS;
else
numIntervals = divResult.quot;
if (SLPGetProperty("com.sun.slp.noSA") && !SDstrcasecmp(SLPGetProperty("com.sun.slp.noSA"),"true"))
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG,"get_converge_result: Simulate converge time out");
#endif
return SLP_OK;
}
for (loop = 0; loop < numIntervals-1; loop++)
plWaits[loop] = (tMaxWaitSecs/(numIntervals-1));
plWaits[numIntervals-1] = 0;
FD_ZERO(&allset);
FD_SET(sd,&allset);
pthread_mutex_lock( &gMulticastTTLLock );
unsigned char defaultTTL = 255;
char temp[16] = {0};
if (SLPGetProperty("net.slp.multicastTTL"))
defaultTTL = (unsigned char) strtol(SLPGetProperty("net.slp.multicastTTL"), &endPtr, 10);
sprintf( temp, "%d", ttl );
SLPSetProperty("net.slp.multicastTTL",temp);
sprintf( temp, "%d", defaultTTL );
if ((err = set_multicast_sender_interf(sd)) != SLP_OK)
{
CLOSESOCKET(sd);
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG,"get_converge_result: set_multicast_sender_interf, %s",slperror(err));
#endif
SLPSetProperty("net.slp.multicastTTL",temp);
pthread_mutex_unlock( &gMulticastTTLLock );
return err;
}
SLPSetProperty("net.slp.multicastTTL",temp);
pthread_mutex_unlock( &gMulticastTTLLock );
while (1)
{
if (!time_remaining(lTimeStart,tMaxwait,plWaits,&sendit,numsent,&tv))
{
iLast = SLP_TRUE;
break;
}
if (sendit )
{
#ifdef USE_PR_LIST
if ( (pcPRList != NULL && pcPrevPRList == NULL)
|| (pcPRList != NULL && pcPrevPRList != NULL && SDstrcasecmp(pcPRList,pcPrevPRList) ) )
{
char* endPtr = NULL;
int mtu = 1400;
if ( SLPGetProperty("net.slp.MTU") )
mtu = strtol(SLPGetProperty("net.slp.MTU"),&endPtr,10);
if ( recalc_sendBuf(pcSendBuf,mtu,pcPRList) == 0 )
{
SLPFree(pcPrevPRList);
pcPrevPRList = safe_malloc(strlen(pcPRList)+1,pcPRList,strlen(pcPRList));
}
else
prListMaxedOut = true;
iSendSz = GETLEN(pcSendBuf);
}
#endif
numsent++;
#ifdef ENABLE_SLP_LOGGING
#ifndef NDEBUG
if ( getenv("SLPTRACE") )
{
sprintf(errbuf,"trace: SEND IT %d\n",numsent);
SLP_LOG( SLP_LOG_DEBUG,errbuf);
}
#endif
sprintf(errbuf,"get_converge_result: sent request to [%s]",inet_ntoa(sin.sin_addr));
SLP_LOG( SLP_LOG_DEBUG,errbuf);
#endif
if (sendto(sd, pcSendBuf, iSendSz, 0, (struct sockaddr*) &sin, sizeof(struct sockaddr_in)) < 0)
{
#ifdef ENABLE_SLP_LOGGING
mslplog(SLP_LOG_DROP,"get_converge_result: multicast sendto",strerror(errno));
#endif
err = SLP_NETWORK_ERROR;
break;
}
}
fds = allset;
result = select(sd+1,&fds,NULL,NULL,&tv);
if (result < 0)
{
if ( errno == EINTR )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DROP, "get_converge_result: select received EINTR");
#endif
continue;
}
else
{
#ifdef ENABLE_SLP_LOGGING
mslplog(SLP_LOG_DEBUG,"get_converge_result: select",strerror(errno));
#endif
err = SLP_NETWORK_ERROR;
break;
}
}
else if (result == 0)
{
long lElapsed = SDGetTime() - lTimeStart;
if (lElapsed < 0) lElapsed *= -1;
if (lElapsed > tMaxwait)
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG,"get_converge_result: ran out of time");
#endif
iLast = SLP_TRUE;
break;
}
else
{
continue;
}
}
else
{
int iInLen = sizeof(struct sockaddr_in);
iErr = recvfrom(sd,pcRecvBuf,iRecvSz,0,(struct sockaddr*)&insin,&iInLen);
if (iErr >= 0 && iErr < HDRLEN+4)
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DROP, "get_converge_result: recvd something, smaller than a SLPv2 msg");
#endif
continue;
}
else if (iErr < 0)
{
#ifdef ENABLE_SLP_LOGGING
if ( errno == EINTR )
{
SLP_LOG( SLP_LOG_DROP, "get_converge_result: recvfrom received EINTR");
}
else
mslplog(SLP_LOG_DROP,"get_converge_result: recvfrom failed",strerror(errno));
#endif
continue;
}
len = iErr;
#ifdef ENABLE_SLP_LOGGING
#ifndef NDEBUG
sprintf(errbuf," received %d byte message from [%s]\n", len, inet_ntoa(insin.sin_addr));
SLP_LOG( SLP_LOG_DEBUG,errbuf);
#endif
#endif
char* tcpBuffer = NULL;
if ( GETFLAGS(pcRecvBuf) & OVERFLOWFLAG )
{
#ifdef ENABLE_SLP_LOGGING
sprintf(errbuf,"get_converge_result: overflow bit set, need to resend TCP request to [%s]",inet_ntoa(insin.sin_addr));
SLP_LOG( SLP_LOG_MSG, errbuf );
#endif
insin.sin_port = htons(SLP_PORT);
if ((err=get_tcp_result(pcSendBuf,iSendSz, insin, &tcpBuffer,&len)) != SLP_OK)
{
sprintf(errbuf,"get_converge_result: error resending TCP request to [%s]: %s",inet_ntoa(insin.sin_addr), slperror(err));
SLPLOG(SLP_LOG_ERR,errbuf);
}
}
if ( tcpBuffer )
{
err=process_reply(pcSendBuf,tcpBuffer,len,&iLast, pvUser,hSLP,pvCallback,cbt);
free( tcpBuffer );
}
else
err=process_reply(pcSendBuf,pcRecvBuf,len,&iLast, pvUser,hSLP,pvCallback,cbt);
if (iLast == 1)
{
break;
}
else if (err == SLP_OK && !prListMaxedOut)
{
#ifdef USE_PR_LIST
prlist_modify(&pcPRList,insin);
#endif
}
else if ( err == SLP_REPLY_DOESNT_MATCH_REQUEST )
{
#ifdef ENABLE_SLP_LOGGING
char errMsg[128];
sprintf( errMsg, "received wrong reply to our request from: %s", inet_ntoa(insin.sin_addr) );
SLP_LOG( SLP_LOG_DEBUG, errMsg );
#endif
}
else if ( err == SLP_REQUEST_CANCELED_BY_USER )
{
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG, "get_converge_result canceled by caller" );
#endif
err = SLP_OK; break;
}
}
}
#ifdef ENABLE_SLP_LOGGING
if ( getenv("SLPTRACE") )
SLP_LOG( SLP_LOG_DEBUG,"trace: no time left\n");
#endif
#ifdef USE_PR_LIST
SLPFree(pcPrevPRList);
SLPFree(pcPRList);
#endif
return err;
}
static SLPBoolean time_remaining(long lTimeStart, time_t tMaxwait,
long *plWaits, int *piSend, int iNumSent, struct timeval *ptv)
{
int i;
long lSum = 0;
long lSoFar = SDGetTime() - lTimeStart;
long lTillNext = 0;
long lDelay = 0;
#ifdef ENABLE_SLP_LOGGING
char errbuf[120];
#endif
*piSend = 0;
if (lSoFar < 0) lSoFar *= -1;
if (lSoFar > tMaxwait) return SLP_FALSE;
for (i = 0; plWaits[i] != 0; i++)
{
lSum += plWaits[i];
lTillNext = lSum - lSoFar;
if (lTillNext < 0)
{
#ifdef ENABLE_SLP_LOGGING
#ifndef NDEBUG
if ( getenv("SLPTRACE") )
{
sprintf(errbuf, "trace: add in another wait interval iSum %ld < lSoFar %ld\n", lSum,lSoFar);
SLP_LOG( SLP_LOG_DEBUG,errbuf);
}
#endif
#endif
continue;
}
if (iNumSent <= i)
*piSend = 1;
#ifdef ENABLE_SLP_LOGGING
#ifndef NDEBUG
if ( getenv("SLPTRACE") )
{
sprintf(errbuf,
"trace: lSoFar = %ld lTillNext = %ld "
"tMaxwait = %ld lSum = %ld: \n",
lSoFar, lTillNext, tMaxwait, lSum);
SLP_LOG( SLP_LOG_DEBUG,errbuf);
}
#endif
#endif
if (lSoFar < lSum)
{
if (tMaxwait < lSum)
{
lDelay = (tMaxwait - lSoFar);
#ifdef ENABLE_SLP_LOGGING
SLP_LOG( SLP_LOG_DEBUG," max < sum.\n");
#endif
break;
}
else
{
lDelay = lTillNext;
break;
}
}
}
if (lDelay == 0)
return SLP_FALSE;
ptv->tv_sec = lDelay;
ptv->tv_usec = 0;
#ifdef ENABLE_SLP_LOGGING
#ifndef NDEBUG
if ( getenv("SLPTRACE") )
{
sprintf(errbuf,"trace: delay %ld sec %ld usec\n",
(unsigned long)ptv->tv_sec, (unsigned long)ptv->tv_usec);
SLP_LOG( SLP_LOG_DEBUG,errbuf);
}
#endif
#endif
return SLP_TRUE;
}