mirror of
https://github.com/moparisthebest/socat
synced 2024-11-10 11:05:04 -05:00
669 lines
21 KiB
C
669 lines
21 KiB
C
/* source: sysutils.c */
|
|
/* Copyright Gerhard Rieger 2001-2008 */
|
|
/* 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"
|
|
|
|
|
|
#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);
|
|
#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);
|
|
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
|
|
char *sockaddr_info(const struct sockaddr *sa, socklen_t salen, char *buff, size_t blen) {
|
|
union sockaddr_union *sau = (union sockaddr_union *)sa;
|
|
char *lbuff = buff;
|
|
char *cp = lbuff;
|
|
int n;
|
|
|
|
#if HAVE_STRUCT_SOCKADDR_SALEN
|
|
if ((n = snprintf(cp, blen, "LEN=%d ", sau->soa.sa_len)) < 0) {
|
|
Warn1("sockaddr_info(): buffer too short ("F_Zu")", blen);
|
|
*buff = '\0';
|
|
return buff;
|
|
}
|
|
cp += n, blen -= n;
|
|
#endif
|
|
if ((n = snprintf(cp, blen, "AF=%d ", sau->soa.sa_family)) < 0) {
|
|
Warn1("sockaddr_info(): buffer too short ("F_Zu")", blen);
|
|
*buff = '\0';
|
|
return buff;
|
|
}
|
|
cp += n, blen -= n;
|
|
|
|
switch (sau->soa.sa_family) {
|
|
#if WITH_UNIX
|
|
case 0:
|
|
case AF_UNIX: sockaddr_unix_info(&sau->un, salen, cp+1, blen-1);
|
|
cp[0] = '"';
|
|
*strchr(cp+1, '\0') = '"';
|
|
break;
|
|
#endif
|
|
#if WITH_IP4
|
|
case AF_INET: sockaddr_inet4_info(&sau->ip4, cp, blen);
|
|
break;
|
|
#endif
|
|
#if WITH_IP6
|
|
case AF_INET6: sockaddr_inet6_info(&sau->ip6, cp, blen);
|
|
break;
|
|
#endif
|
|
default:
|
|
if ((n = snprintf(cp, blen, "AF=%d ", sa->sa_family)) < 0) {
|
|
Warn1("sockaddr_info(): buffer too short ("F_Zu")", blen);
|
|
*buff = '\0';
|
|
return buff;
|
|
}
|
|
cp += n, blen -= n;
|
|
if ((snprintf(cp, blen,
|
|
"0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
|
((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])) < 0) {
|
|
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) {
|
|
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);
|
|
*nextc = '\0';
|
|
strncpy(buff, ubuff, blen);
|
|
} else
|
|
#endif /* WITH_ABSTRACT_UNIXSOCKET */
|
|
{
|
|
nextc =
|
|
sanitize_string(sa->sun_path,
|
|
MIN(UNIX_PATH_MAX, strlen(sa->sun_path)),
|
|
ubuff, XIOSAN_DEFAULT_BACKSLASH_OCT_3);
|
|
*nextc = '\0';
|
|
strncpy(buff, ubuff, blen);
|
|
}
|
|
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 (snprintf(buff, blen, "%u.%u.%u.%u",
|
|
(unsigned int)(addr >> 24), (unsigned int)((addr >> 16) & 0xff),
|
|
(unsigned int)((addr >> 8) & 0xff), (unsigned int)(addr & 0xff)) < 0) {
|
|
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 (snprintf(buff, blen, "%u.%u.%u.%u:%hu",
|
|
((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)) < 0) {
|
|
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 =
|
|
snprintf(addrtext, textlen, "%u.%u.%u.%u",
|
|
((unsigned char *)binaddr)[0],
|
|
((unsigned char *)binaddr)[1],
|
|
((unsigned char *)binaddr)[2],
|
|
((unsigned char *)binaddr)[3]))
|
|
< 0) {
|
|
return NULL; /* errno is valid */
|
|
}
|
|
break;
|
|
#if WITH_IP6
|
|
case PF_INET6:
|
|
if ((retlen =
|
|
snprintf(addrtext, textlen, "%x:%x:%x:%x:%x:%x:%x:%x",
|
|
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])
|
|
))
|
|
< 0) {
|
|
return NULL; /* errno is valid */
|
|
}
|
|
break;
|
|
#endif /* WITH_IP6 */
|
|
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
|
|
least 50 chars long. output includes the port number */
|
|
char *sockaddr_inet6_info(const struct sockaddr_in6 *sa, char *buff, size_t blen) {
|
|
if (snprintf(buff, blen, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%hu",
|
|
#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)) < 0) {
|
|
Warn("sockaddr_inet6_info(): buffer too short");
|
|
buff[blen-1] = '\0';
|
|
}
|
|
return buff;
|
|
}
|
|
#endif /* WITH_IP6 */
|
|
|
|
/* fill the list with the supplementary group ids of user.
|
|
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, size_t *ngroups) {
|
|
struct group *grp;
|
|
size_t i = 0;
|
|
|
|
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;
|
|
}
|
|
|
|
#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 */
|
|
|
|
|
|
/* this function behaves like poll(). It tries to do so even when the poll()
|
|
system call is not available. */
|
|
/* 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); }
|
|
}
|
|
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;
|
|
}
|
|
#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);
|
|
#else /* HAVE_POLL */
|
|
} else {
|
|
Error("poll() not available");
|
|
return -1;
|
|
#endif /* !HAVE_POLL */
|
|
}
|
|
}
|
|
|
|
|
|
#if WITH_TCP || WITH_UDP
|
|
/* returns port in network byte order;
|
|
ipproto==IPPROTO_UDP resolves as UDP service, every other value resolves as
|
|
TCP */
|
|
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;
|
|
}
|
|
|
|
if ((se = getservbyname(portname, ipproto==IPPROTO_UDP?"udp":"tcp")) == NULL) {
|
|
Error2("cannot resolve service \"%s/%d\"", portname, ipproto);
|
|
return 0;
|
|
}
|
|
|
|
return se->s_port;
|
|
}
|
|
#endif /* WITH_TCP || WITH_UDP */
|
|
|
|
|
|
#if WITH_IP4 || WITH_IP6 || WITH_INTERFACE
|
|
/* 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) {
|
|
/* 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*/
|
|
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) {
|
|
Error1("socket(PF_INET, SOCK_DGRAM, IPPROTO_IP): %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
|
|
if (Ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
|
|
Info3("ioctl(%d, SIOCGIFINDEX, {\"%s\"}): %s",
|
|
s, ifr.ifr_name, strerror(errno));
|
|
Close(s);
|
|
return -1;
|
|
}
|
|
Close(s);
|
|
#if HAVE_STRUCT_IFREQ_IFR_INDEX
|
|
Info3("ioctl(%d, SIOCGIFINDEX, {\"%s\"}) -> { %d }",
|
|
s, ifname, ifr.ifr_index);
|
|
return ifr.ifr_index;
|
|
#elif HAVE_STRUCT_IFREQ_IFR_IFINDEX
|
|
Info3("ioctl(%d, SIOCGIFINDEX, {\"%s\"}) -> { %d }",
|
|
s, ifname, ifr.ifr_ifindex);
|
|
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 */
|
|
|
|
|
|
#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.
|
|
writes the resulting index to *ifindex and returns 0,
|
|
or returns -1 on error */
|
|
int ifindex(const char *ifname, unsigned int *ifindex, int anysock) {
|
|
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) {
|
|
return -1;
|
|
}
|
|
*ifindex = val;
|
|
return 0;
|
|
}
|
|
#endif /* WITH_IP4 || WITH_IP6 || WITH_INTERFACE */
|
|
|
|
|
|
/* constructs an environment variable whose name is built from socats uppercase
|
|
program name, and underscore and varname; if a variable of this name already
|
|
exists a non zero value of overwrite lets the old value be overwritten.
|
|
returns 0 on success or <0 if an error occurred. */
|
|
int xiosetenv(const char *varname, const char *value, int overwrite) {
|
|
# define XIO_ENVNAMELEN 256
|
|
const char *progname;
|
|
char envname[XIO_ENVNAMELEN];
|
|
size_t i, l;
|
|
|
|
progname = diag_get_string('p');
|
|
strncpy(envname, progname, XIO_ENVNAMELEN-1);
|
|
l = strlen(progname);
|
|
strncpy(envname+l, "_", XIO_ENVNAMELEN-1-l);
|
|
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
|
|
strncpy(envname+l+1, varname, XIO_ENVNAMELEN-1-l);
|
|
if (Setenv(envname, value, 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;
|
|
# undef XIO_ENVNAMELEN
|
|
}
|
|
|
|
int xiosetenv2(const char *varname, const char *varname2, const char *value,
|
|
int overwrite) {
|
|
# define XIO_ENVNAMELEN 256
|
|
const char *progname;
|
|
char envname[XIO_ENVNAMELEN];
|
|
size_t i, l;
|
|
|
|
progname = diag_get_string('p');
|
|
strncpy(envname, progname, XIO_ENVNAMELEN-1);
|
|
l = strlen(progname);
|
|
strncpy(envname+l, "_", XIO_ENVNAMELEN-1-l);
|
|
l += 1;
|
|
strncpy(envname+l, varname, XIO_ENVNAMELEN-1-l);
|
|
l += strlen(varname);
|
|
strncpy(envname+l, "_", XIO_ENVNAMELEN-1-l);
|
|
l += 1;
|
|
strncpy(envname+l, varname2, XIO_ENVNAMELEN-1-l);
|
|
l += strlen(varname2);
|
|
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
|
|
if (Setenv(envname, value, 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;
|
|
# 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);
|
|
# 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);
|
|
# undef XIO_SHORTLEN
|
|
}
|