#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <slp-internal.h>
#include <slp_net_utils.h>
static SLPBoolean tcp_thr_running = SLP_FALSE;
static slp_queue_t *tcp_q;
static int tcp_sockfd;
static mutex_t start_lock = DEFAULTMUTEX;
struct tcp_rqst {
slp_handle_impl_t *hp;
slp_target_t *target;
const char *scopes;
SLPBoolean free_target;
unsigned short xid;
};
struct bc_ifs {
struct sockaddr_in *sin;
int num_ifs;
};
static SLPError start_tcp_thr();
static void *tcp_thread(void *);
static SLPError make_header(slp_handle_impl_t *, char *, const char *);
static void udp_make_msghdr(struct sockaddr_in *, struct iovec *, int,
struct msghdr *);
static SLPError make_mc_target(slp_handle_impl_t *,
struct sockaddr_in *, char *,
struct pollfd **, nfds_t *, struct bc_ifs *);
static SLPError make_bc_target(slp_handle_impl_t *, struct in_addr *,
int, struct bc_ifs *);
static SLPError mc_sendmsg(struct pollfd *, struct msghdr *,
struct bc_ifs *);
static SLPError bc_sendmsg(struct pollfd *, struct msghdr *, struct bc_ifs *);
static void mc_recvmsg(struct pollfd *, nfds_t, slp_handle_impl_t *,
const char *, char *, void **, unsigned long long,
unsigned long long, unsigned long long *,
int *, int *, int);
static void free_pfds(struct pollfd *, nfds_t);
static void tcp_handoff(slp_handle_impl_t *, const char *,
struct sockaddr_in *, unsigned short);
static unsigned long long now_millis();
static int wait_for_response(unsigned long long, int *,
unsigned long long, unsigned long long *,
struct pollfd [], nfds_t);
static int add2pr_list(slp_msg_t *, struct sockaddr_in *, void **);
static void free_pr_node(void *, VISIT, int, void *);
void slp_uc_tcp_send(slp_handle_impl_t *hp, slp_target_t *target,
const char *scopes, SLPBoolean free_target,
unsigned short xid) {
struct tcp_rqst *rqst;
if (!hp->tcp_lock) {
if (!(hp->tcp_lock = malloc(sizeof (*(hp->tcp_lock))))) {
slp_err(LOG_CRIT, 0, "slp_uc_tcp_send",
"out of memory");
return;
}
(void) mutex_init(hp->tcp_lock, USYNC_THREAD, NULL);
}
if (!hp->tcp_wait) {
if (!(hp->tcp_wait = malloc(sizeof (*(hp->tcp_wait))))) {
slp_err(LOG_CRIT, 0, "slp_uc_tcp_send",
"out of memory");
return;
}
(void) cond_init(hp->tcp_wait, USYNC_THREAD, NULL);
}
(void) mutex_lock(hp->tcp_lock);
(hp->tcp_ref_cnt)++;
(void) mutex_unlock(hp->tcp_lock);
if (!tcp_thr_running)
if (start_tcp_thr() != SLP_OK)
return;
if (!(rqst = malloc(sizeof (*rqst)))) {
slp_err(LOG_CRIT, 0, "slp_uc_tcp_send", "out of memory");
return;
}
rqst->hp = hp;
rqst->target = target;
rqst->scopes = scopes;
rqst->free_target = free_target;
rqst->xid = xid;
(void) slp_enqueue(tcp_q, rqst);
}
void slp_tcp_wait(slp_handle_impl_t *hp) {
(void) mutex_lock(hp->tcp_lock);
while (hp->tcp_ref_cnt > 0)
(void) cond_wait(hp->tcp_wait, hp->tcp_lock);
(void) mutex_unlock(hp->tcp_lock);
}
void slp_uc_udp_send(slp_handle_impl_t *hp, slp_target_t *target,
const char *scopes) {
slp_target_t *ctarg;
struct sockaddr_in *sin;
struct msghdr msg[1];
char header[SLP_DEFAULT_SENDMTU];
int sockfd;
size_t mtu;
SLPBoolean use_tcp;
struct pollfd pfd[1];
unsigned long long now, sent;
char *reply = NULL;
use_tcp = SLP_FALSE;
if (make_header(hp, header, scopes) != SLP_OK)
return;
mtu = slp_get_mtu();
for (ctarg = target; ctarg; ctarg = slp_next_failover(ctarg)) {
char *state;
const char *timeouts;
int timeout;
sin = (struct sockaddr_in *)slp_get_target_sin(ctarg);
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
slp_err(LOG_CRIT, 0, "slp_uc_udp_send",
"could not create socket: %s",
strerror(errno));
return;
}
pfd[0].fd = sockfd;
pfd[0].events = POLLRDNORM;
udp_make_msghdr(sin, hp->msg.iov, hp->msg.iovlen, msg);
if (!reply && !(reply = malloc(mtu))) {
(void) close(sockfd);
slp_err(LOG_CRIT, 0, "slp_uc_udp_send",
"out of memory");
return;
}
timeouts = SLPGetProperty(SLP_CONFIG_DATAGRAMTIMEOUTS);
state = (char *)timeouts;
for (timeout = slp_get_next_onlist(&state);
timeout != -1 &&
!hp->cancel;
timeout = slp_get_next_onlist(&state)) {
int pollerr;
if (sendmsg(sockfd, msg, 0) < 0) {
slp_err(LOG_CRIT, 0, "slp_uc_udp_send",
"sendmsg failed: %s", strerror(errno));
continue;
}
sent = now_millis();
pollerr = wait_for_response(
0, &timeout, sent, &now, pfd, 1);
if (pollerr == 0)
continue;
if (pollerr < 0)
break;
if (recvfrom(sockfd, reply, mtu, 0, NULL, NULL) < 0) {
if (errno == ENOMEM) {
free(reply); reply = NULL;
use_tcp = SLP_TRUE;
break;
}
slp_err(LOG_CRIT, 0, "slp_uc_udp_send",
"recvfrom failed: %s",
strerror(errno));
} else {
slp_proto_err errcode = slp_get_errcode(reply);
switch (errcode) {
case SLP_MSG_PARSE_ERROR:
case SLP_VER_NOT_SUPPORTED:
case SLP_SICK_DA:
case SLP_DA_BUSY_NOW:
case SLP_OPTION_NOT_UNDERSTOOD:
case SLP_RQST_NOT_SUPPORTED: {
char addrbuf[INET6_ADDRSTRLEN], *cname;
cname = slp_ntop(addrbuf, INET6_ADDRSTRLEN,
(const void *) &(sin->sin_addr));
cname = cname ? cname : "[invalid addr]";
slp_err(LOG_INFO, 0,
"DA %s returned error code %d; dropping reply",
cname, errcode);
free(reply); reply = NULL;
}
}
}
break;
}
if (timeout != -1)
break;
slp_mark_target_failed(ctarg);
}
(void) close(sockfd);
if (!ctarg || hp->cancel) {
if (reply) free(reply);
return;
}
if (reply) {
if (slp_get_overflow(reply))
use_tcp = SLP_TRUE;
else
slp_mark_target_used(ctarg);
(void) slp_enqueue(hp->q, reply);
}
if (use_tcp)
slp_uc_tcp_send(
hp, ctarg, scopes, SLP_FALSE, slp_get_xid(header));
}
void slp_mc_send(slp_handle_impl_t *hp, const char *scopes) {
char header[SLP_DEFAULT_SENDMTU], *state;
const char *timeouts;
struct sockaddr_in sin[1];
struct msghdr msg[1];
int maxwait, timeout, noresults, anyresults;
unsigned long long final_to, now, sent;
struct pollfd *pfd;
nfds_t nfds;
void *collator = NULL;
struct bc_ifs bcifs;
if (make_header(hp, header, scopes) != SLP_OK)
return;
(void) memset(sin, 0, sizeof (sin));
if (make_mc_target(hp, sin, header, &pfd, &nfds, &bcifs) != SLP_OK)
return;
udp_make_msghdr(sin, hp->msg.iov, hp->msg.iovlen, msg);
maxwait = slp_get_mcmaxwait();
maxwait = maxwait ? maxwait : SLP_DEFAULT_MAXWAIT;
now = now_millis();
final_to = now + maxwait;
timeouts = SLPGetProperty(SLP_CONFIG_MULTICASTTIMEOUTS);
state = (char *)timeouts;
noresults = anyresults = 0;
for (timeout = slp_get_next_onlist(&state);
timeout != -1 &&
now < final_to &&
noresults < 2 &&
!hp->cancel;
timeout = slp_get_next_onlist(&state)) {
if (mc_sendmsg(pfd, msg, &bcifs) != SLP_OK) {
continue;
}
sent = now_millis();
mc_recvmsg(pfd, nfds, hp, scopes, header, &collator, final_to,
sent, &now, &noresults, &anyresults, timeout);
if (!anyresults)
noresults++;
anyresults = 0;
}
if (collator)
slp_twalk(collator, free_pr_node, 0, NULL);
free_pfds(pfd, nfds);
if (bcifs.sin) free(bcifs.sin);
}
static SLPError
start_tcp_thr(void)
{
SLPError err;
int terr;
(void) mutex_lock(&start_lock);
if (tcp_thr_running) {
(void) mutex_unlock(&start_lock);
return (SLP_OK);
}
if (!(tcp_q = slp_new_queue(&err))) {
(void) mutex_unlock(&start_lock);
return (err);
}
if ((terr = thr_create(0, 0, tcp_thread, NULL, 0, NULL)) != 0) {
slp_err(LOG_CRIT, 0, "start_tcp_thr",
"could not start thread: %s", strerror(terr));
(void) mutex_unlock(&start_lock);
return (SLP_INTERNAL_SYSTEM_ERROR);
}
tcp_thr_running = SLP_TRUE;
(void) mutex_unlock(&start_lock);
return (SLP_OK);
}
static void end_tcp_thr() {
(void) mutex_lock(&start_lock);
tcp_thr_running = SLP_FALSE;
slp_destroy_queue(tcp_q);
(void) mutex_unlock(&start_lock);
thr_exit(NULL);
}
static void *
tcp_thread(void *arg __unused)
{
struct tcp_rqst *rqst;
char *reply, header[SLP_DEFAULT_SENDMTU];
timestruc_t to[1];
to->tv_nsec = 0;
for (;;) {
slp_target_t *ctarg, *targets;
slp_handle_impl_t *hp;
const char *scopes;
struct sockaddr_in *sin;
SLPBoolean free_target, etimed;
unsigned short xid;
to->tv_sec = time(NULL) + 30;
if (!(rqst = slp_dequeue_timed(tcp_q, to, &etimed))) {
if (!etimed)
continue;
else
end_tcp_thr();
}
hp = rqst->hp;
scopes = rqst->scopes;
targets = rqst->target;
free_target = rqst->free_target;
xid = rqst->xid;
free(rqst);
reply = NULL;
if (hp->cancel)
goto transaction_complete;
if (make_header(hp, header, scopes) != SLP_OK) {
if (free_target) slp_free_target(targets);
continue;
}
if (xid)
slp_set_xid(header, xid);
for (ctarg = targets; ctarg && !hp->cancel;
ctarg = slp_next_failover(ctarg)) {
sin = (struct sockaddr_in *)slp_get_target_sin(ctarg);
if ((tcp_sockfd = socket(AF_INET, SOCK_STREAM, 0))
< 0) {
slp_err(LOG_CRIT, 0, "tcp_thread",
"could not create socket: %s",
strerror(errno));
ctarg = NULL;
break;
}
if (connect(tcp_sockfd, (struct sockaddr *)sin,
sizeof (*sin)) < 0) {
slp_err(LOG_INFO, 0, "tcp_thread",
"could not connect, error = %s",
strerror(errno));
goto failed;
}
if (writev(tcp_sockfd, hp->msg.iov, hp->msg.iovlen)
== -1) {
slp_err(LOG_INFO, 0, "tcp_thread",
"could not send, error = %s",
strerror(errno));
goto failed;
}
if ((slp_tcp_read(tcp_sockfd, &reply)) == SLP_OK) {
(void) close(tcp_sockfd);
break;
}
failed:
(void) close(tcp_sockfd);
slp_mark_target_failed(ctarg);
}
if (hp->cancel) {
if (reply) {
free(reply);
}
} else if (ctarg) {
(void) slp_enqueue(hp->q, reply);
slp_mark_target_used(ctarg);
}
transaction_complete:
(void) mutex_lock(hp->tcp_lock);
if (--(hp->tcp_ref_cnt) == 0)
(void) cond_signal(hp->tcp_wait);
(void) mutex_unlock(hp->tcp_lock);
if (free_target)
slp_free_target(targets);
}
return (NULL);
}
SLPError slp_tcp_read(int sockfd, char **reply) {
char lenbuf[5], *p;
size_t nleft;
ssize_t nread;
unsigned int len;
nleft = 5;
p = lenbuf;
while (nleft != 0) {
if ((nread = read(sockfd, p, 5)) < 0) {
if (errno == EINTR)
nread = 0;
else
return (SLP_NETWORK_ERROR);
} else if (nread == 0)
return (SLP_NETWORK_ERROR);
nleft -= nread;
p += nread;
}
len = slp_get_length(lenbuf);
if (!(*reply = malloc(len))) {
slp_err(LOG_CRIT, 0, "tcp_read", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
(void) memcpy(*reply, lenbuf, 5);
nleft = len - 5;
p = *reply + 5;
while (nleft != 0) {
if ((nread = read(sockfd, p, nleft)) < 0) {
if (errno == EINTR)
nread = 0;
else {
free(*reply);
return (SLP_NETWORK_ERROR);
}
} else if (nread == 0)
return (SLP_OK);
nleft -= nread;
p += nread;
}
return (SLP_OK);
}
static SLPError make_header(slp_handle_impl_t *hp, char *header,
const char *scopes) {
SLPError err;
size_t msgLen, off;
int i;
size_t mtu;
unsigned short slen = (unsigned short)strlen(scopes);
mtu = slp_get_mtu();
msgLen = slp_hdrlang_length(hp);
hp->msg.iov[0].iov_base = header;
hp->msg.iov[0].iov_len = msgLen;
hp->msg.prlist->iov_base = header + msgLen;
for (i = 1; i < hp->msg.iovlen; i++) {
msgLen += hp->msg.iov[i].iov_len;
}
msgLen += slen;
off = 0;
if ((err = slp_add_header(hp->locale, header, mtu,
hp->fid, msgLen, &off)) != SLP_OK)
return (err);
hp->msg.prlist->iov_len = 0;
off = 0;
if ((err = slp_add_sht((char *)hp->msg.scopeslen.iov_base,
2, slen, &off)) != SLP_OK) {
return (err);
}
hp->msg.scopes->iov_base = (caddr_t)scopes;
hp->msg.scopes->iov_len = slen;
return (SLP_OK);
}
static void udp_make_msghdr(struct sockaddr_in *sin, struct iovec *iov,
int iovlen, struct msghdr *msg) {
msg->msg_name = (caddr_t)sin;
msg->msg_namelen = 16;
msg->msg_iov = iov;
msg->msg_iovlen = iovlen;
msg->msg_accrights = NULL;
msg->msg_accrightslen = 0;
}
static SLPError make_mc_target(slp_handle_impl_t *hp,
struct sockaddr_in *sin, char *header,
struct pollfd **fds, nfds_t *nfds,
struct bc_ifs *bcifs) {
unsigned char ttl = slp_get_multicastTTL();
char *ifs_string;
SLPBoolean have_valid_if = SLP_FALSE;
SLPBoolean use_broadcast = slp_get_usebroadcast();
int fd, i, num_givenifs;
struct in_addr *given_ifs = NULL;
nfds_t nfd_i;
sin->sin_port = htons(SLP_PORT);
sin->sin_family = AF_INET;
slp_set_mcast(header);
bcifs->sin = NULL;
*fds = NULL;
if ((ifs_string = (char *)SLPGetProperty(
SLP_CONFIG_INTERFACES)) != NULL && *ifs_string) {
char *p, *tstate;
p = strchr(ifs_string, ',');
for (num_givenifs = 1; p; num_givenifs++) {
p = strchr(p + 1, ',');
}
if (!(given_ifs = calloc(num_givenifs, sizeof (*given_ifs)))) {
slp_err(LOG_CRIT, 0, "make_mc_target",
"out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
i = 0;
if (!(ifs_string = strdup(ifs_string))) {
slp_err(LOG_CRIT, 0, "make_mc_target",
"out of memory");
free(given_ifs);
return (SLP_MEMORY_ALLOC_FAILED);
}
for (
p = strtok_r(ifs_string, ",", &tstate);
p;
p = strtok_r(NULL, ",", &tstate)) {
if (slp_pton(p, &(given_ifs[i])) < 1) {
num_givenifs--;
continue;
}
i++;
}
*nfds = num_givenifs;
free(ifs_string);
if (!(*fds = calloc(num_givenifs, sizeof (**fds)))) {
slp_err(LOG_CRIT, 0, "make_mc_target",
"out of memory");
free(ifs_string);
free(given_ifs);
return (SLP_MEMORY_ALLOC_FAILED);
}
for (i = 0; i < num_givenifs; i++) {
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
slp_err(LOG_CRIT, 0, "make_mc_target",
"could not create socket: %s",
strerror(errno));
free_pfds(*fds, *nfds);
return (SLP_INTERNAL_SYSTEM_ERROR);
}
(*fds)[i].fd = fd;
(*fds)[i].events |= POLLRDNORM;
if (use_broadcast) {
struct sockaddr_in bcsin[1];
(void) memcpy(
&(bcsin->sin_addr), &(given_ifs[i]),
sizeof (bcsin->sin_addr));
bcsin->sin_family = AF_INET;
bcsin->sin_port = 0;
if (bind(fd, (struct sockaddr *)bcsin,
sizeof (*bcsin)) == 0) {
continue;
}
slp_err(LOG_INFO, 0, "make_mc_target",
"could not set broadcast interface: %s",
strerror(errno));
}
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
&(given_ifs[i]), sizeof (given_ifs[i]))
< 0) {
slp_err(LOG_INFO, 0, "make_mc_target",
"could not set multicast interface: %s",
strerror(errno));
continue;
}
have_valid_if = SLP_TRUE;
}
if (use_broadcast) {
SLPError err;
if ((err = make_bc_target(
hp, given_ifs, num_givenifs, bcifs))
!= SLP_OK) {
if (err == SLP_MEMORY_ALLOC_FAILED) {
return (err);
}
have_valid_if = SLP_FALSE;
}
}
free(given_ifs);
}
if (!have_valid_if) {
if (*fds && !have_valid_if) {
free(*fds);
}
if (!(*fds = calloc(1, sizeof (**fds)))) {
slp_err(LOG_CRIT, 0, "make_mc_target",
"out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
slp_err(LOG_CRIT, 0, "make_mc_target",
"could not create socket: %s",
strerror(errno));
free(*fds);
return (SLP_INTERNAL_SYSTEM_ERROR);
}
(**fds).fd = fd;
(**fds).events |= POLLRDNORM;
*nfds = 1;
}
for (nfd_i = 0; nfd_i < *nfds; nfd_i++) {
if (use_broadcast) {
const int on = 1;
if (setsockopt((*fds)[nfd_i].fd, SOL_SOCKET,
SO_BROADCAST,
(void *) &on, sizeof (on)) < 0) {
slp_err(LOG_CRIT, 0, "make_mc_target",
"could not enable broadcast: %s",
strerror(errno));
}
} else {
if (setsockopt((*fds)[nfd_i].fd, IPPROTO_IP,
IP_MULTICAST_TTL, &ttl, 1) < 0) {
slp_err(LOG_CRIT, 0, "make_mc_target",
"could not set multicast TTL: %s",
strerror(errno));
}
}
}
if (use_broadcast) {
sin->sin_addr.s_addr = INADDR_BROADCAST;
} else {
sin->sin_addr.s_addr = SLP_MULTICAST_ADDRESS;
}
return (SLP_OK);
}
static SLPError make_bc_target(slp_handle_impl_t *hp,
struct in_addr *given_ifs,
int num_givenifs, struct bc_ifs *bcifs) {
SLPError err;
int i;
if ((err = slp_broadcast_addrs(hp, given_ifs, num_givenifs,
&(bcifs->sin), &(bcifs->num_ifs)))
!= SLP_OK) {
return (err);
}
for (i = 0; i < bcifs->num_ifs; i++) {
bcifs->sin[i].sin_port = htons(SLP_PORT);
}
return (SLP_OK);
}
static SLPError mc_sendmsg(struct pollfd *fds,
struct msghdr *msg, struct bc_ifs *bcifs) {
if (slp_get_usebroadcast()) {
char *ifs = (char *)SLPGetProperty(SLP_CONFIG_INTERFACES);
if (ifs && *ifs && bc_sendmsg(fds, msg, bcifs) == SLP_OK) {
return (SLP_OK);
}
}
if (sendmsg(fds[0].fd, msg, 0) < 0) {
slp_err(LOG_CRIT, 0, "mc_sendmsg",
"sendmsg failed: %s", strerror(errno));
return (SLP_NETWORK_ERROR);
}
return (SLP_OK);
}
static SLPError bc_sendmsg(struct pollfd *fds, struct msghdr *msg,
struct bc_ifs *bcifs) {
int i;
SLPBoolean sent_one = SLP_FALSE;
for (i = 0; i < bcifs->num_ifs; i++) {
msg->msg_name = (caddr_t)&(bcifs->sin[i]);
if (sendmsg(fds[0].fd, msg, 0) < 0) {
slp_err(LOG_CRIT, 0, "bc_sendmsg",
"sendmsg failed: %s", strerror(errno));
continue;
}
sent_one = SLP_TRUE;
}
return (sent_one ? SLP_OK : SLP_NETWORK_ERROR);
}
static void mc_recvmsg(struct pollfd *pfd, nfds_t nfds, slp_handle_impl_t *hp,
const char *scopes, char *header, void **collator,
unsigned long long final_to,
unsigned long long sent,
unsigned long long *now,
int *noresults, int *anyresults, int timeout) {
char *reply = NULL;
nfds_t i;
struct sockaddr_in responder;
int pollerr;
socklen_t addrlen = sizeof (responder);
size_t mtu = slp_get_mtu();
for (; !hp->cancel; ) {
pollerr = wait_for_response(
final_to, &timeout, sent, now, pfd, nfds);
if (pollerr == 0)
goto cleanup;
if (pollerr < 0)
goto cleanup;
for (i = 0; !hp->cancel && i < nfds; i++) {
if (pfd[i].fd < 0 ||
!(pfd[i].revents & (POLLRDNORM | POLLERR))) {
continue;
}
if (!reply && !(reply = malloc(mtu))) {
slp_err(LOG_CRIT, 0, "mc_revcmsg", "out of memory");
return;
}
if (recvfrom(pfd[i].fd, reply, mtu, 0,
(struct sockaddr *)&responder,
(int *)&addrlen) < 0) {
if (errno == ENOMEM) {
free(reply); reply = NULL;
tcp_handoff(hp, scopes,
&responder, slp_get_xid(header));
continue;
}
slp_err(LOG_CRIT, 0, "mc_recvmsg",
"recvfrom failed: %s",
strerror(errno));
continue;
} else {
if (slp_get_overflow(reply)) {
tcp_handoff(hp, scopes,
&responder, slp_get_xid(header));
}
if (add2pr_list(&(hp->msg), &responder, collator)) {
(void) slp_enqueue(hp->q, reply);
*noresults = 0;
*anyresults = 1;
reply = NULL;
}
*now = now_millis();
if (*now > final_to)
goto cleanup;
}
}
timeout = timeout - (int)(*now - sent);
if (timeout <= 0) {
goto cleanup;
}
}
cleanup:
if (reply) {
free(reply);
}
}
static void free_pfds(struct pollfd *pfds, nfds_t nfds) {
nfds_t i;
for (i = 0; i < nfds; i++) {
if (pfds[i].fd <= 0) {
continue;
}
(void) close(pfds[i].fd);
}
free(pfds);
}
static void tcp_handoff(slp_handle_impl_t *hp, const char *scopes,
struct sockaddr_in *sin, unsigned short xid) {
slp_target_t *target;
target = slp_fabricate_target(sin);
slp_uc_tcp_send(hp, target, scopes, SLP_TRUE, xid);
}
static unsigned long long now_millis() {
unsigned long long i;
struct timeval tv[1];
(void) gettimeofday(tv, NULL);
i = (unsigned long long) tv->tv_sec * 1000;
i += tv->tv_usec / 1000;
return (i);
}
static int wait_for_response(
unsigned long long final_to,
int *timeout,
unsigned long long sent,
unsigned long long *now,
struct pollfd pfd[], nfds_t nfds) {
int when, pollerr;
for (;;) {
pollerr = poll(pfd, nfds, *timeout);
*now = now_millis();
if (pollerr > 0)
return (pollerr);
if (pollerr == 0)
return (0);
if (pollerr < 0)
if (errno == EAGAIN || errno == EINTR) {
when = (int)(*now - sent);
if (
(final_to != 0 && *now > final_to) ||
when > *timeout)
break;
*timeout = *timeout - when;
continue;
} else {
slp_err(LOG_INFO, 0, "wait for response",
"poll error: %s",
strerror(errno));
return (pollerr);
}
}
return (0);
}
static int add2pr_list(
slp_msg_t *msg,
struct sockaddr_in *sin,
void **collator) {
char **res, *cname, *p, *header;
size_t mtu;
size_t len, off, namelen;
unsigned short prlen;
if (!(cname = slp_gethostbyaddr((char *)&(sin->sin_addr),
sizeof (sin->sin_addr))))
return (0);
res = slp_tsearch(
cname, collator,
(int (*)(const void *, const void *)) strcasecmp);
if (*res != cname) {
slp_err(LOG_INFO, 0, "add2pr_list",
"drop PR ignored by host: %s",
cname);
free(cname);
return (0);
}
mtu = slp_get_mtu();
header = msg->iov[0].iov_base;
len = slp_get_length(header);
namelen = strlen(cname);
if ((namelen + 2 + len) >= mtu)
return (1);
prlen = (unsigned short)msg->prlist->iov_len;
p = msg->prlist->iov_base + prlen;
*p = 0;
if (prlen) {
namelen++;
(void) strcat(p, ",");
}
(void) strcat(p, cname);
len += namelen;
slp_set_length(header, len);
prlen += (unsigned short)namelen;
off = 0;
(void) slp_add_sht(msg->prlistlen.iov_base, 2, prlen, &off);
msg->prlist->iov_len += namelen;
return (1);
}
static void free_pr_node(void *node, VISIT order, int level, void *cookie) {
if (order == endorder || order == leaf) {
char *pr = *(char **)node;
free(pr);
free(node);
}
}