#include <alloca.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/types.h>
#include <sys/isa_defs.h>
#include "ipmp.h"
#include "ipmp_mpathd.h"
int
ipmp_connect(int *fdp)
{
int fd;
int error;
int on = 1;
int flags;
struct sockaddr_in sin;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
return (IPMP_FAILURE);
(void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on));
(void) memset(&sin, 0, sizeof (struct sockaddr_in));
sin.sin_port = htons(0);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1)
goto fail;
sin.sin_port = htons(MPATHD_PORT);
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (connect(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
if (errno == ECONNREFUSED) {
(void) close(fd);
return (IPMP_ENOMPATHD);
}
goto fail;
}
flags = fcntl(fd, F_GETFL, 0);
if (flags != -1)
(void) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
*fdp = fd;
return (IPMP_SUCCESS);
fail:
error = errno;
(void) close(fd);
errno = error;
return (IPMP_FAILURE);
}
int
ipmp_readtlv(int fd, ipmp_infotype_t *typep, size_t *lenp, void **valuep,
const struct timeval *endtp)
{
int retval;
void *value;
uint32_t tlen;
retval = ipmp_read(fd, typep, sizeof (*typep), endtp);
if (retval != IPMP_SUCCESS)
return (retval);
retval = ipmp_read(fd, &tlen, sizeof (tlen), endtp);
if (retval != IPMP_SUCCESS)
return (retval);
*lenp = tlen;
value = malloc(*lenp);
if (value == NULL) {
value = alloca(*lenp);
(void) ipmp_read(fd, value, *lenp, endtp);
return (IPMP_ENOMEM);
}
retval = ipmp_read(fd, value, *lenp, endtp);
if (retval != IPMP_SUCCESS) {
free(value);
return (retval);
}
*valuep = value;
return (IPMP_SUCCESS);
}
int
ipmp_write(int fd, const void *buffer, size_t buflen)
{
size_t nwritten;
ssize_t nbytes;
const char *buf = buffer;
for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
nbytes = write(fd, &buf[nwritten], buflen - nwritten);
if (nbytes == -1)
return (IPMP_FAILURE);
if (nbytes == 0) {
errno = EIO;
return (IPMP_FAILURE);
}
}
assert(nwritten == buflen);
return (IPMP_SUCCESS);
}
int
ipmp_writetlv(int fd, ipmp_infotype_t type, size_t len, void *value)
{
int retval;
uint32_t tlen;
#if defined(_LP64)
if (len > UINT32_MAX)
return (IPMP_EPROTO);
#endif
tlen = (uint32_t)len;
retval = ipmp_write(fd, &type, sizeof (type));
if (retval != IPMP_SUCCESS)
return (retval);
retval = ipmp_write(fd, &tlen, sizeof (uint32_t));
if (retval != IPMP_SUCCESS)
return (retval);
return (ipmp_write(fd, value, tlen));
}
int
ipmp_read(int fd, void *buffer, size_t buflen, const struct timeval *endtp)
{
int retval;
int timeleft = -1;
struct timeval curtime;
ssize_t nbytes = 0;
ssize_t prbytes;
struct pollfd pfd;
while (nbytes < buflen) {
if (endtp != NULL) {
if (gettimeofday(&curtime, NULL) == -1)
break;
timeleft = (endtp->tv_sec - curtime.tv_sec) * MILLISEC;
timeleft += (endtp->tv_usec - curtime.tv_usec) / 1000;
if (timeleft < 0)
timeleft = 0;
}
pfd.fd = fd;
pfd.events = POLLIN;
retval = poll(&pfd, 1, timeleft);
if (retval <= 0) {
if (retval == 0)
errno = ETIME;
break;
}
prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes);
if (prbytes <= 0) {
if (prbytes == -1 && errno == EINTR)
continue;
break;
}
nbytes += prbytes;
}
return (nbytes == buflen ? IPMP_SUCCESS : IPMP_FAILURE);
}