socat/sysutils.c

788 lines
25 KiB
C
Raw Permalink Normal View History

2008-01-28 21:37:16 +00:00
/* source: sysutils.c */
/* Copyright Gerhard Rieger */
2008-01-27 12:00:08 +00:00
/* Published under the GNU General Public License V.2, see file COPYING */
/* translate socket addresses into human readable form */
#include "config.h"
#include "xioconfig.h"
#include "sysincludes.h"
#include "compat.h" /* socklen_t */
#include "mytypes.h"
#include "error.h"
#include "sycls.h"
#include "utils.h"
#include "sysutils.h"
/* Substitute for Write():
Try to write all bytes before returning; this handles EINTR,
EAGAIN/EWOULDBLOCK, and partial write situations. The drawback is that this
function might block even with O_NONBLOCK option.
Returns <0 on unhandled error, errno valid
Will only return <0 or bytes
*/
ssize_t writefull(int fd, const void *buff, size_t bytes) {
size_t writt = 0;
ssize_t chk;
while (1) {
chk = Write(fd, (const char *)buff + writt, bytes - writt);
if (chk < 0) {
switch (errno) {
case EINTR:
case EAGAIN:
#if EAGAIN != EWOULDBLOCK
case EWOULDBLOCK:
#endif
Warn4("write(%d, %p, "F_Zu"): %s", fd, (const char *)buff+writt, bytes-writt, strerror(errno));
Sleep(1); continue;
default: return -1;
}
} else if (writt+chk < bytes) {
Warn4("write(%d, %p, "F_Zu"): only wrote "F_Zu" bytes, trying to continue ",
fd, (const char *)buff+writt, bytes-writt, chk);
writt += chk;
} else {
writt = bytes;
break;
}
}
return writt;
}
2008-01-27 12:00:08 +00:00
#if WITH_UNIX
void socket_un_init(struct sockaddr_un *sa) {
#if HAVE_STRUCT_SOCKADDR_SALEN
sa->sun_len = sizeof(struct sockaddr_un);
#endif
sa->sun_family = AF_UNIX;
memset(sa->sun_path, '\0', sizeof(sa->sun_path));
}
#endif /* WITH_UNIX */
#if WITH_IP4
void socket_in_init(struct sockaddr_in *sa) {
#if HAVE_STRUCT_SOCKADDR_SALEN
sa->sin_len = sizeof(struct sockaddr_in);
#endif
sa->sin_family = AF_INET;
sa->sin_port = 0;
sa->sin_addr.s_addr = 0;
sa->sin_zero[0] = 0;
sa->sin_zero[1] = 0;
sa->sin_zero[2] = 0;
sa->sin_zero[3] = 0;
sa->sin_zero[4] = 0;
sa->sin_zero[5] = 0;
sa->sin_zero[6] = 0;
sa->sin_zero[7] = 0;
}
#endif /* WITH_IP4 */
#if WITH_IP6
void socket_in6_init(struct sockaddr_in6 *sa) {
#if HAVE_STRUCT_SOCKADDR_SALEN
sa->sin6_len = sizeof(struct sockaddr_in6);
#endif
sa->sin6_family = AF_INET6;
sa->sin6_port = 0;
sa->sin6_flowinfo = 0;
#if HAVE_IP6_SOCKADDR==0
sa->sin6_addr.s6_addr[0] = 0;
sa->sin6_addr.s6_addr[1] = 0;
sa->sin6_addr.s6_addr[2] = 0;
sa->sin6_addr.s6_addr[3] = 0;
sa->sin6_addr.s6_addr[4] = 0;
sa->sin6_addr.s6_addr[5] = 0;
sa->sin6_addr.s6_addr[6] = 0;
sa->sin6_addr.s6_addr[7] = 0;
sa->sin6_addr.s6_addr[8] = 0;
sa->sin6_addr.s6_addr[9] = 0;
sa->sin6_addr.s6_addr[10] = 0;
sa->sin6_addr.s6_addr[11] = 0;
sa->sin6_addr.s6_addr[12] = 0;
sa->sin6_addr.s6_addr[13] = 0;
sa->sin6_addr.s6_addr[14] = 0;
sa->sin6_addr.s6_addr[15] = 0;
#elif HAVE_IP6_SOCKADDR==1
sa->sin6_addr.u6_addr.u6_addr32[0] = 0;
sa->sin6_addr.u6_addr.u6_addr32[1] = 0;
sa->sin6_addr.u6_addr.u6_addr32[2] = 0;
sa->sin6_addr.u6_addr.u6_addr32[3] = 0;
#elif HAVE_IP6_SOCKADDR==2
sa->sin6_addr.u6_addr32[0] = 0;
sa->sin6_addr.u6_addr32[1] = 0;
sa->sin6_addr.u6_addr32[2] = 0;
sa->sin6_addr.u6_addr32[3] = 0;
#elif HAVE_IP6_SOCKADDR==3
sa->sin6_addr.in6_u.u6_addr32[0] = 0;
sa->sin6_addr.in6_u.u6_addr32[1] = 0;
sa->sin6_addr.in6_u.u6_addr32[2] = 0;
sa->sin6_addr.in6_u.u6_addr32[3] = 0;
#elif HAVE_IP6_SOCKADDR==4
sa->sin6_addr._S6_un._S6_u32[0] = 0;
sa->sin6_addr._S6_un._S6_u32[1] = 0;
sa->sin6_addr._S6_un._S6_u32[2] = 0;
sa->sin6_addr._S6_un._S6_u32[3] = 0;
#elif HAVE_IP6_SOCKADDR==5
sa->sin6_addr.__u6_addr.__u6_addr32[0] = 0;
sa->sin6_addr.__u6_addr.__u6_addr32[1] = 0;
sa->sin6_addr.__u6_addr.__u6_addr32[2] = 0;
sa->sin6_addr.__u6_addr.__u6_addr32[3] = 0;
#endif
}
#endif /* WITH_IP6 */
#if _WITH_SOCKET
/* initializes the socket address of the specified address family. Returns the
length of the specific socket address, or 0 on error. */
socklen_t socket_init(int af, union sockaddr_union *sa) {
switch (af) {
case AF_UNSPEC: memset(sa, 0, sizeof(*sa)); return sizeof(*sa);
2008-01-27 12:00:08 +00:00
#if WITH_UNIX
case AF_UNIX: socket_un_init(&sa->un); return sizeof(sa->un);
#endif
#if WITH_IP4
case AF_INET: socket_in_init(&sa->ip4); return sizeof(sa->ip4);
#endif
#if WITH_IP6
case AF_INET6: socket_in6_init(&sa->ip6); return sizeof(sa->ip6);
#endif
default: Info1("socket_init(): unknown address family %d", af);
2008-01-27 12:00:08 +00:00
memset(sa, 0, sizeof(union sockaddr_union));
sa->soa.sa_family = af;
return 0;
}
}
#endif /* _WITH_SOCKET */
#if WITH_UNIX
#define XIOUNIXSOCKOVERHEAD (sizeof(struct sockaddr_un)-sizeof(((struct sockaddr_un*)0)->sun_path))
#endif
#if _WITH_SOCKET
/* writes a textual human readable representation of the sockaddr contents to buff and returns a pointer to buff
writes at most blen bytes to buff including the terminating \0 byte
*/
2008-01-27 12:00:08 +00:00
char *sockaddr_info(const struct sockaddr *sa, socklen_t salen, char *buff, size_t blen) {
2008-09-22 20:17:55 +00:00
union sockaddr_union *sau = (union sockaddr_union *)sa;
2008-01-27 12:00:08 +00:00
char *lbuff = buff;
char *cp = lbuff;
int n;
#if HAVE_STRUCT_SOCKADDR_SALEN
n = xio_snprintf(cp, blen, "LEN=%d ", sau->soa.sa_len);
if (n < 0 || n >= blen) {
Warn1("sockaddr_info(): buffer too short ("F_Zu")", blen);
*buff = '\0';
return buff;
}
cp += n, blen -= n;
#endif
n = xio_snprintf(cp, blen, "AF=%d ", sau->soa.sa_family);
if (n < 0 || n >= blen) {
2008-01-27 12:00:08 +00:00
Warn1("sockaddr_info(): buffer too short ("F_Zu")", blen);
*buff = '\0';
return buff;
}
cp += n, blen -= n;
2008-09-22 20:17:55 +00:00
switch (sau->soa.sa_family) {
2008-01-27 12:00:08 +00:00
#if WITH_UNIX
case 0:
2008-09-22 20:17:55 +00:00
case AF_UNIX: sockaddr_unix_info(&sau->un, salen, cp+1, blen-1);
cp[0] = '"';
strncat(cp+1, "\"", 1);
2008-01-27 12:00:08 +00:00
break;
#endif
#if WITH_IP4
2008-09-22 20:17:55 +00:00
case AF_INET: sockaddr_inet4_info(&sau->ip4, cp, blen);
2008-01-27 12:00:08 +00:00
break;
#endif
#if WITH_IP6
2008-09-22 20:17:55 +00:00
case AF_INET6: sockaddr_inet6_info(&sau->ip6, cp, blen);
2008-01-27 12:00:08 +00:00
break;
#endif
default:
n = xio_snprintf(cp, blen, "AF=%d ", sa->sa_family);
if (n < 0 || n >= blen) {
2008-09-22 20:17:55 +00:00
Warn1("sockaddr_info(): buffer too short ("F_Zu")", blen);
*buff = '\0';
return buff;
}
cp += n, blen -= n;
n = xio_snprintf(cp, blen,
2008-01-27 12:00:08 +00:00
"0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
2008-10-12 10:01:00 +00:00
((unsigned char *)sau->soa.sa_data)[0],
((unsigned char *)sau->soa.sa_data)[1],
((unsigned char *)sau->soa.sa_data)[2],
((unsigned char *)sau->soa.sa_data)[3],
((unsigned char *)sau->soa.sa_data)[4],
((unsigned char *)sau->soa.sa_data)[5],
((unsigned char *)sau->soa.sa_data)[6],
((unsigned char *)sau->soa.sa_data)[7],
((unsigned char *)sau->soa.sa_data)[8],
((unsigned char *)sau->soa.sa_data)[9],
((unsigned char *)sau->soa.sa_data)[10],
((unsigned char *)sau->soa.sa_data)[11],
((unsigned char *)sau->soa.sa_data)[12],
((unsigned char *)sau->soa.sa_data)[13]);
if (n < 0 || n >= blen) {
2008-01-27 12:00:08 +00:00
Warn("sockaddr_info(): buffer too short");
*buff = '\0';
return buff;
}
}
return lbuff;
}
#endif /* _WITH_SOCKET */
#if WITH_UNIX
char *sockaddr_unix_info(const struct sockaddr_un *sa, socklen_t salen, char *buff, size_t blen) {
2008-09-22 20:17:55 +00:00
char ubuff[5*UNIX_PATH_MAX+3];
char *nextc;
#if WITH_ABSTRACT_UNIXSOCKET
if (salen > XIOUNIXSOCKOVERHEAD &&
sa->sun_path[0] == '\0') {
nextc =
sanitize_string(sa->sun_path, salen-XIOUNIXSOCKOVERHEAD,
ubuff, XIOSAN_DEFAULT_BACKSLASH_OCT_3);
} else
#endif /* WITH_ABSTRACT_UNIXSOCKET */
{
if (salen <= XIOUNIXSOCKOVERHEAD) {
nextc = sanitize_string ("<anon>", MIN(UNIX_PATH_MAX, strlen("<anon>")),
ubuff, XIOSAN_DEFAULT_BACKSLASH_OCT_3);
} else {
nextc = sanitize_string(sa->sun_path,
MIN(UNIX_PATH_MAX, strlen(sa->sun_path)),
ubuff, XIOSAN_DEFAULT_BACKSLASH_OCT_3);
}
2008-01-27 12:00:08 +00:00
}
*nextc = '\0';
buff[0] = '\0'; strncat(buff, ubuff, blen-1);
2008-01-27 12:00:08 +00:00
return buff;
}
#endif /* WITH_UNIX */
#if WITH_IP4
/* addr in host byte order! */
char *inet4addr_info(uint32_t addr, char *buff, size_t blen) {
if (xio_snprintf(buff, blen, "%u.%u.%u.%u",
2008-01-27 12:00:08 +00:00
(unsigned int)(addr >> 24), (unsigned int)((addr >> 16) & 0xff),
(unsigned int)((addr >> 8) & 0xff), (unsigned int)(addr & 0xff)) >= blen) {
2008-01-27 12:00:08 +00:00
Warn("inet4addr_info(): buffer too short");
buff[blen-1] = '\0';
}
return buff;
}
#endif /* WITH_IP4 */
#if WITH_IP4
char *sockaddr_inet4_info(const struct sockaddr_in *sa, char *buff, size_t blen) {
if (xio_snprintf(buff, blen, "%u.%u.%u.%u:%hu",
2008-01-27 12:00:08 +00:00
((unsigned char *)&sa->sin_addr.s_addr)[0],
((unsigned char *)&sa->sin_addr.s_addr)[1],
((unsigned char *)&sa->sin_addr.s_addr)[2],
((unsigned char *)&sa->sin_addr.s_addr)[3],
htons(sa->sin_port)) >= blen) {
2008-01-27 12:00:08 +00:00
Warn("sockaddr_inet4_info(): buffer too short");
buff[blen-1] = '\0';
}
return buff;
}
#endif /* WITH_IP4 */
#if !HAVE_INET_NTOP
/* http://www.opengroup.org/onlinepubs/000095399/functions/inet_ntop.html */
const char *inet_ntop(int pf, const void *binaddr,
char *addrtext, socklen_t textlen) {
size_t retlen;
switch (pf) {
case PF_INET:
if ((retlen =
xio_snprintf(addrtext, textlen, "%u.%u.%u.%u",
2008-01-27 12:00:08 +00:00
((unsigned char *)binaddr)[0],
((unsigned char *)binaddr)[1],
((unsigned char *)binaddr)[2],
((unsigned char *)binaddr)[3]))
>= textlen) {
errno = ENOSPC; return NULL;
2008-01-27 12:00:08 +00:00
}
break;
2008-10-12 10:01:00 +00:00
#if WITH_IP6
2008-01-27 12:00:08 +00:00
case PF_INET6:
if ((retlen =
xio_snprintf(addrtext, textlen, "%x:%x:%x:%x:%x:%x:%x:%x",
2008-01-27 12:00:08 +00:00
ntohs(((uint16_t *)binaddr)[0]),
ntohs(((uint16_t *)binaddr)[1]),
ntohs(((uint16_t *)binaddr)[2]),
ntohs(((uint16_t *)binaddr)[3]),
ntohs(((uint16_t *)binaddr)[4]),
ntohs(((uint16_t *)binaddr)[5]),
ntohs(((uint16_t *)binaddr)[6]),
ntohs(((uint16_t *)binaddr)[7])
))
>= textlen) {
errno = ENOSPC; return NULL;
2008-01-27 12:00:08 +00:00
}
break;
2008-10-12 10:01:00 +00:00
#endif /* WITH_IP6 */
2008-01-27 12:00:08 +00:00
default:
errno = EAFNOSUPPORT;
return NULL;
}
addrtext[retlen] = '\0';
return addrtext;
}
#endif /* !HAVE_INET_NTOP */
#if WITH_IP6
/* convert the IP6 socket address to human readable form. buff should be at
2008-09-22 20:17:55 +00:00
least 50 chars long. output includes the port number */
2008-01-27 12:00:08 +00:00
char *sockaddr_inet6_info(const struct sockaddr_in6 *sa, char *buff, size_t blen) {
if (xio_snprintf(buff, blen, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%hu",
2008-01-27 12:00:08 +00:00
#if HAVE_IP6_SOCKADDR==0
(sa->sin6_addr.s6_addr[0]<<8)+
sa->sin6_addr.s6_addr[1],
(sa->sin6_addr.s6_addr[2]<<8)+
sa->sin6_addr.s6_addr[3],
(sa->sin6_addr.s6_addr[4]<<8)+
sa->sin6_addr.s6_addr[5],
(sa->sin6_addr.s6_addr[6]<<8)+
sa->sin6_addr.s6_addr[7],
(sa->sin6_addr.s6_addr[8]<<8)+
sa->sin6_addr.s6_addr[9],
(sa->sin6_addr.s6_addr[10]<<8)+
sa->sin6_addr.s6_addr[11],
(sa->sin6_addr.s6_addr[12]<<8)+
sa->sin6_addr.s6_addr[13],
(sa->sin6_addr.s6_addr[14]<<8)+
sa->sin6_addr.s6_addr[15],
#elif HAVE_IP6_SOCKADDR==1
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr.u6_addr16)[0]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr.u6_addr16)[1]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr.u6_addr16)[2]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr.u6_addr16)[3]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr.u6_addr16)[4]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr.u6_addr16)[5]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr.u6_addr16)[6]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr.u6_addr16)[7]),
#elif HAVE_IP6_SOCKADDR==2
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr16)[0]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr16)[1]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr16)[2]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr16)[3]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr16)[4]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr16)[5]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr16)[6]),
ntohs(((unsigned short *)&sa->sin6_addr.u6_addr16)[7]),
#elif HAVE_IP6_SOCKADDR==3
ntohs(((unsigned short *)&sa->sin6_addr.in6_u.u6_addr16)[0]),
ntohs(((unsigned short *)&sa->sin6_addr.in6_u.u6_addr16)[1]),
ntohs(((unsigned short *)&sa->sin6_addr.in6_u.u6_addr16)[2]),
ntohs(((unsigned short *)&sa->sin6_addr.in6_u.u6_addr16)[3]),
ntohs(((unsigned short *)&sa->sin6_addr.in6_u.u6_addr16)[4]),
ntohs(((unsigned short *)&sa->sin6_addr.in6_u.u6_addr16)[5]),
ntohs(((unsigned short *)&sa->sin6_addr.in6_u.u6_addr16)[6]),
ntohs(((unsigned short *)&sa->sin6_addr.in6_u.u6_addr16)[7]),
#elif HAVE_IP6_SOCKADDR==4
(sa->sin6_addr._S6_un._S6_u8[0]<<8)|(sa->sin6_addr._S6_un._S6_u8[1]&0xff),
(sa->sin6_addr._S6_un._S6_u8[2]<<8)|(sa->sin6_addr._S6_un._S6_u8[3]&0xff),
(sa->sin6_addr._S6_un._S6_u8[4]<<8)|(sa->sin6_addr._S6_un._S6_u8[5]&0xff),
(sa->sin6_addr._S6_un._S6_u8[6]<<8)|(sa->sin6_addr._S6_un._S6_u8[7]&0xff),
(sa->sin6_addr._S6_un._S6_u8[8]<<8)|(sa->sin6_addr._S6_un._S6_u8[9]&0xff),
(sa->sin6_addr._S6_un._S6_u8[10]<<8)|(sa->sin6_addr._S6_un._S6_u8[11]&0xff),
(sa->sin6_addr._S6_un._S6_u8[12]<<8)|(sa->sin6_addr._S6_un._S6_u8[13]&0xff),
(sa->sin6_addr._S6_un._S6_u8[14]<<8)|(sa->sin6_addr._S6_un._S6_u8[15]&0xff),
#elif HAVE_IP6_SOCKADDR==5
ntohs(((unsigned short *)&sa->sin6_addr.__u6_addr.__u6_addr16)[0]),
ntohs(((unsigned short *)&sa->sin6_addr.__u6_addr.__u6_addr16)[1]),
ntohs(((unsigned short *)&sa->sin6_addr.__u6_addr.__u6_addr16)[2]),
ntohs(((unsigned short *)&sa->sin6_addr.__u6_addr.__u6_addr16)[3]),
ntohs(((unsigned short *)&sa->sin6_addr.__u6_addr.__u6_addr16)[4]),
ntohs(((unsigned short *)&sa->sin6_addr.__u6_addr.__u6_addr16)[5]),
ntohs(((unsigned short *)&sa->sin6_addr.__u6_addr.__u6_addr16)[6]),
ntohs(((unsigned short *)&sa->sin6_addr.__u6_addr.__u6_addr16)[7]),
#endif
ntohs(sa->sin6_port)) >= blen) {
2008-01-27 12:00:08 +00:00
Warn("sockaddr_inet6_info(): buffer too short");
}
return buff;
}
#endif /* WITH_IP6 */
#if HAVE_GETGROUPLIST || (defined(HAVE_SETGRENT) && defined(HAVE_GETGRENT) && defined(HAVE_ENDGRENT))
/* fills the list with the supplementary group ids of user.
2008-01-27 12:00:08 +00:00
caller passes size of list in ngroups, function returns number of groups in
ngroups.
function returns 0 if 0 or more groups were found, or 1 if the list is too
short. */
int getusergroups(const char *user, gid_t *list, int *ngroups) {
#if HAVE_GETGROUPLIST
/* we prefer getgrouplist because it may be much faster with many groups, but it is not standard */
gid_t grp, twogrps[2];
int two = 2;
/* getgrouplist requires to pass an extra group id, typically the users primary group, that is then added to the supplementary group list. We don't want such an additional group in the result, but there is not "unspecified" gid value available. Thus we try to find an abitrary supplementary group id that we then pass in a second call to getgrouplist. */
grp = 0;
Getgrouplist(user, grp, twogrps, &two);
if (two == 1) {
/* either user has just this supp group, or none; we try another id */
grp = 1; two = 2;
Getgrouplist(user, grp, twogrps, &two);
if (two == 1) {
/* user has no supp group */
*ngroups = 0;
return 0;
}
/* user has just the first tried group */
*ngroups = 1; list[0] = grp;
return 0;
}
/* find the first supp group that is not our grp, and use its id */
if (twogrps[0] == grp) {
grp = twogrps[1];
} else {
grp = twogrps[0];
}
if (Getgrouplist(user, grp, list, ngroups) < 0) {
return 1;
}
return 0;
#elif defined(HAVE_SETGRENT) && defined(HAVE_GETGRENT) && defined(HAVE_ENDGRENT)
/* this is standard (POSIX) but may be slow */
2008-01-27 12:00:08 +00:00
struct group *grp;
int i = 0;
2008-01-27 12:00:08 +00:00
setgrent();
while (grp = getgrent()) {
char **gusr = grp->gr_mem;
while (*gusr) {
if (!strcmp(*gusr, user)) {
if (i == *ngroups)
return 1;
list[i++] = grp->gr_gid;
break;
}
++gusr;
}
}
endgrent();
*ngroups = i;
return 0;
#endif /* HAVE_SETGRENT... */
2008-01-27 12:00:08 +00:00
}
2011-11-22 09:58:15 +00:00
#endif
2008-01-27 12:00:08 +00:00
#if !HAVE_HSTRERROR
const char *hstrerror(int err) {
static const char *h_messages[] = {
"success",
"authoritative answer not found",
"non-authoritative, host not found, or serverfail",
"Host name lookup failure", /* "non recoverable error" */
"valid name, no data record of requested type" };
assert(HOST_NOT_FOUND==1);
assert(TRY_AGAIN==2);
assert(NO_RECOVERY==3);
assert(NO_DATA==4);
if ((err < 0) || err > sizeof(h_messages)/sizeof(const char *)) {
return "";
}
return h_messages[err];
}
#endif /* !HAVE_HSTRERROR */
2008-07-24 19:51:38 +00:00
/* this function behaves like poll(). It tries to do so even when the poll()
system call is not available. */
2008-10-12 10:01:00 +00:00
/* note: glibc 5.4 does not know nfds_t */
int xiopoll(struct pollfd fds[], unsigned long nfds, struct timeval *timeout) {
int i, n = 0;
int result = 0;
while (true) { /* should be if (), but we want to break */
fd_set readfds;
fd_set writefds;
fd_set exceptfds;
FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
for (i = 0; i < nfds; ++i) {
fds[i].revents = 0;
if (fds[i].fd < 0) { continue; }
if (fds[i].fd > FD_SETSIZE) { break; /* use poll */ }
if (fds[i].events & POLLIN) {
FD_SET(fds[i].fd, &readfds); n = MAX(n, fds[i].fd); }
if (fds[i].events & POLLOUT) {
FD_SET(fds[i].fd, &writefds); n = MAX(n, fds[i].fd); }
}
2008-10-12 10:01:00 +00:00
if (i < nfds) { break; /* use poll */ }
result = Select(n+1, &readfds, &writefds, &exceptfds, timeout);
if (result < 0) { return result; }
for (i = 0; i < nfds; ++i) {
if (fds[i].fd < 0) { continue; }
if ((fds[i].events & POLLIN) && FD_ISSET(fds[i].fd, &readfds)) {
fds[i].revents |= POLLIN; ++result;
}
if ((fds[i].events & POLLOUT) && FD_ISSET(fds[i].fd, &writefds)) {
fds[i].revents |= POLLOUT; ++result;
}
}
return result;
}
2008-07-24 19:51:38 +00:00
#if HAVE_POLL
{
int ms = 0;
if (timeout == NULL) {
ms = -1;
} else {
ms = 1000*timeout->tv_sec + timeout->tv_usec/1000;
}
/*! timeout */
return Poll(fds, nfds, ms);
2008-07-24 19:51:38 +00:00
#else /* HAVE_POLL */
} else {
Error("poll() not available");
return -1;
2008-07-24 19:51:38 +00:00
#endif /* !HAVE_POLL */
}
2008-07-24 19:51:38 +00:00
}
2008-01-27 12:00:08 +00:00
#if WITH_TCP || WITH_UDP
2008-09-22 21:21:26 +00:00
/* returns port in network byte order;
ipproto==IPPROTO_UDP resolves as UDP service, every other value resolves as
TCP */
2008-01-27 12:00:08 +00:00
int parseport(const char *portname, int ipproto) {
struct servent *se;
char *extra;
int result;
if (isdigit(portname[0]&0xff)) {
result = htons(strtoul(portname, &extra, 0));
if (*extra != '\0') {
Error3("parseport(\"%s\", %d): extra trailing data \"%s\"",
portname, ipproto, extra);
}
return result;
}
2008-09-22 21:21:26 +00:00
if ((se = getservbyname(portname, ipproto==IPPROTO_UDP?"udp":"tcp")) == NULL) {
2008-01-27 12:00:08 +00:00
Error2("cannot resolve service \"%s/%d\"", portname, ipproto);
return 0;
}
return se->s_port;
}
#endif /* WITH_TCP || WITH_UDP */
2008-09-22 20:17:55 +00:00
#if WITH_IP4 || WITH_IP6 || WITH_INTERFACE
2008-01-27 12:00:08 +00:00
/* check the systems interfaces for ifname and return its index
or -1 if no interface with this name was found
The system calls require an arbitrary socket; the calling program may
provide one in anysock to avoid creation of a dummy socket. anysock must be
<0 if it does not specify a socket fd.
*/
int ifindexbyname(const char *ifname, int anysock) {
2008-01-27 12:00:08 +00:00
/* Linux: man 7 netdevice */
/* FreeBSD: man 4 networking */
/* Solaris: man 7 if_tcp */
#if defined(HAVE_STRUCT_IFREQ) && defined(SIOCGIFCONF) && defined(SIOCGIFINDEX)
/* currently we support Linux, FreeBSD; not Solaris */
#define IFBUFSIZ 32*sizeof(struct ifreq) /*1024*/
2008-01-27 12:00:08 +00:00
int s;
struct ifreq ifr;
if (ifname[0] == '\0') {
return -1;
}
if (anysock >= 0) {
s = anysock;
} else if ((s = Socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
2008-01-27 12:00:08 +00:00
Error1("socket(PF_INET, SOCK_DGRAM, IPPROTO_IP): %s", strerror(errno));
return -1;
}
strncpy(ifr.ifr_name, ifname, IFNAMSIZ); /* ok */
2008-01-27 12:00:08 +00:00
if (Ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
Info3("ioctl(%d, SIOCGIFINDEX, {\"%s\"}): %s",
2008-01-27 12:00:08 +00:00
s, ifr.ifr_name, strerror(errno));
Close(s);
2008-01-27 12:00:08 +00:00
return -1;
}
Close(s);
#if HAVE_STRUCT_IFREQ_IFR_INDEX
Info3("ioctl(%d, SIOCGIFINDEX, {\"%s\"}) -> { %d }",
s, ifname, ifr.ifr_index);
2008-01-27 12:00:08 +00:00
return ifr.ifr_index;
#elif HAVE_STRUCT_IFREQ_IFR_IFINDEX
Info3("ioctl(%d, SIOCGIFINDEX, {\"%s\"}) -> { %d }",
s, ifname, ifr.ifr_ifindex);
2008-01-27 12:00:08 +00:00
return ifr.ifr_ifindex;
#endif /* HAVE_STRUCT_IFREQ_IFR_IFINDEX */
#else /* !defined(HAVE_ STRUCT_IFREQ) && defined(SIOCGIFCONF) && defined(SIOCGIFINDEX) */
return -1;
#endif /* !defined(HAVE_ STRUCT_IFREQ) && defined(SIOCGIFCONF) && defined(SIOCGIFINDEX) */
}
#endif /* WITH_IP4 || WITH_IP6 || WITH_INTERFACE */
2008-01-27 12:00:08 +00:00
#if WITH_IP4 || WITH_IP6 || WITH_INTERFACE
/* like ifindexbyname(), but also allows the index number as input - in this
case it does not lookup the index.
2008-01-27 12:00:08 +00:00
writes the resulting index to *ifindex and returns 0,
or returns -1 on error */
int ifindex(const char *ifname, unsigned int *ifindex, int anysock) {
2008-01-27 12:00:08 +00:00
char *endptr;
long int val;
if (ifname[0] == '\0') {
return -1;
}
val = strtol(ifname, &endptr, 0);
if (endptr[0] == '\0') {
*ifindex = val;
return 0;
}
if ((val = ifindexbyname(ifname, anysock)) < 0) {
2008-01-27 12:00:08 +00:00
return -1;
}
*ifindex = val;
return 0;
}
#endif /* WITH_IP4 || WITH_IP6 || WITH_INTERFACE */
2008-09-22 20:17:55 +00:00
int _xiosetenv(const char *envname, const char *value, int overwrite, const char *sep) {
char *oldval;
char *newval;
if (overwrite >= 2 && (oldval = getenv(envname)) != NULL) {
size_t newlen = strlen(oldval)+strlen(sep)+strlen(value)+1;
if ((newval = Malloc(newlen+1)) == NULL) {
return -1;
}
snprintf(newval, newlen+1, "%s%s%s", oldval, sep, value);
} else {
newval = (char *)value;
}
if (Setenv(envname, newval, overwrite) < 0) {
Warn3("setenv(\"%s\", \"%s\", 1): %s",
envname, value, strerror(errno));
#if HAVE_UNSETENV
Unsetenv(envname); /* dont want to have a wrong value */
#endif
return -1;
}
return 0;
}
2008-09-22 20:17:55 +00:00
/* constructs an environment variable whose name is built from socats uppercase
program name, and underscore and varname;
if the variable of this name already exists arg overwrite determines:
0: keep old value
1: overwrite with new value
2: append to old value, separated by *sep
a non zero value of overwrite lets the old value be overwritten.
2008-09-22 20:17:55 +00:00
returns 0 on success or <0 if an error occurred. */
int xiosetenv(const char *varname, const char *value, int overwrite, const char *sep) {
2008-09-22 20:17:55 +00:00
# define XIO_ENVNAMELEN 256
const char *progname;
char envname[XIO_ENVNAMELEN];
size_t i, l;
progname = diag_get_string('p');
envname[0] = '\0'; strncat(envname, progname, XIO_ENVNAMELEN-1);
l = strlen(envname);
2008-09-22 20:17:55 +00:00
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
strncat(envname+l, varname, XIO_ENVNAMELEN-l-1);
return _xiosetenv(envname, value, overwrite, sep);
# undef ENVNAMELEN
2008-09-22 20:17:55 +00:00
}
int xiosetenv2(const char *varname, const char *varname2, const char *value,
int overwrite, const char *sep) {
2008-09-22 20:17:55 +00:00
# define XIO_ENVNAMELEN 256
const char *progname;
char envname[XIO_ENVNAMELEN];
size_t i, l;
progname = diag_get_string('p');
envname[0] = '\0'; strncat(envname, progname, XIO_ENVNAMELEN-1);
2008-09-22 20:17:55 +00:00
l = strlen(progname);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
2008-09-22 20:17:55 +00:00
l += 1;
strncat(envname+l, varname, XIO_ENVNAMELEN-l-1);
l += strlen(envname+l);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
2008-09-22 20:17:55 +00:00
l += 1;
strncat(envname+l, varname2, XIO_ENVNAMELEN-l-1);
l += strlen(envname+l);
2008-09-22 20:17:55 +00:00
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
return _xiosetenv(envname, value, overwrite, sep);
# undef XIO_ENVNAMELEN
}
int xiosetenv3(const char *varname, const char *varname2, const char *varname3,
const char *value,
int overwrite, const char *sep) {
# define XIO_ENVNAMELEN 256
const char *progname;
char envname[XIO_ENVNAMELEN];
size_t i, l;
progname = diag_get_string('p');
envname[0] = '\0'; strncat(envname, progname, XIO_ENVNAMELEN-1);
l = strlen(progname);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
strncat(envname+l, varname, XIO_ENVNAMELEN-l-1);
l += strlen(envname+l);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
strncat(envname+l, varname2, XIO_ENVNAMELEN-l-1);
l += strlen(envname+l);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
strncat(envname+l, varname3, XIO_ENVNAMELEN-l-1);
l += strlen(envname+l);
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
return _xiosetenv(envname, value, overwrite, sep);
2008-09-22 20:17:55 +00:00
# undef XIO_ENVNAMELEN
}
/* like xiosetenv(), but uses an unsigned long value */
int xiosetenvulong(const char *varname, unsigned long value, int overwrite) {
# define XIO_LONGLEN 21 /* should suffice for 64bit longs with \0 */
char envbuff[XIO_LONGLEN];
snprintf(envbuff, XIO_LONGLEN, "%lu", value);
return xiosetenv(varname, envbuff, overwrite, NULL);
2008-09-22 20:17:55 +00:00
# undef XIO_LONGLEN
}
/* like xiosetenv(), but uses an unsigned short value */
int xiosetenvushort(const char *varname, unsigned short value, int overwrite) {
# define XIO_SHORTLEN 11 /* should suffice for 32bit shorts with \0 */
char envbuff[XIO_SHORTLEN];
snprintf(envbuff, XIO_SHORTLEN, "%hu", value);
return xiosetenv(varname, envbuff, overwrite, NULL);
2008-09-22 20:17:55 +00:00
# undef XIO_SHORTLEN
}