| /* source: sysutils.c */ |
| /* Copyright Gerhard Rieger and contributors (see file CHANGES) */ |
| /* 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_INTERFACE |
| const int one = 1; |
| #endif |
| |
| /* 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 (rev.direction is blocked)", |
| fd, (const char *)buff+writt, bytes-writt, chk); |
| writt += chk; |
| } else { |
| writt = bytes; |
| break; |
| } |
| } |
| Notice3("write(%d, %p, "F_Zu") completed", fd, (const char *)buff, bytes); |
| return writt; |
| } |
| |
| #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 |
| /* 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 |
| */ |
| 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 |
| 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) { |
| 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: |
| *cp++ = '"'; --blen; |
| sockaddr_unix_info(&sau->un, salen, cp, blen-1); |
| blen -= strlen(cp); |
| cp = strchr(cp, '\0'); |
| *cp++ = '"'; --blen; |
| if (blen > 0) |
| *cp = '\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 |
| #if WITH_VSOCK |
| case AF_VSOCK: sockaddr_vm_info(&sau->vm, cp, blen); |
| break; |
| #endif |
| default: |
| n = xio_snprintf(cp, blen, "AF=%d ", sa->sa_family); |
| if (n < 0 || n >= blen) { |
| Warn1("sockaddr_info(): buffer too short ("F_Zu")", blen); |
| *buff = '\0'; |
| return buff; |
| } |
| cp += n, blen -= n; |
| n = xio_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]); |
| if (n < 0 || n >= blen) { |
| 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); |
| } 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); |
| } |
| } |
| *nextc = '\0'; |
| buff[0] = '\0'; strncat(buff, ubuff, blen-1); |
| 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", |
| (unsigned int)(addr >> 24), (unsigned int)((addr >> 16) & 0xff), |
| (unsigned int)((addr >> 8) & 0xff), (unsigned int)(addr & 0xff)) >= blen) { |
| 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", |
| ((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) { |
| Warn("sockaddr_inet4_info(): buffer too short"); |
| buff[blen-1] = '\0'; |
| } |
| return buff; |
| } |
| #endif /* WITH_IP4 */ |
| |
| #if WITH_VSOCK |
| char *sockaddr_vm_info(const struct sockaddr_vm *sa, char *buff, size_t blen) { |
| if (xio_snprintf(buff, blen, "cid:%u port:%u", sa->svm_cid, sa->svm_port) >= blen) { |
| Warn("sockaddr_vm_info(): buffer too short"); |
| buff[blen-1] = '\0'; |
| } |
| return buff; |
| } |
| |
| int sockaddr_vm_parse(struct sockaddr_vm *sa, const char *cid_str, |
| const char *port_str) |
| { |
| char *garbage = NULL; |
| if (!cid_str) { |
| sa->svm_cid = VMADDR_CID_ANY; |
| } else { |
| sa->svm_cid = strtoul(cid_str, &garbage, 0); |
| if (*garbage != '\0') { |
| Error1("sockaddr_vm - garbage in cid: \"%s\"", garbage); |
| return -EINVAL; |
| } |
| } |
| |
| if (!port_str) { |
| sa->svm_port = VMADDR_PORT_ANY; |
| } else { |
| sa->svm_port = strtoul(port_str, &garbage, 0); |
| if (*garbage != '\0') { |
| Error1("sockaddr_vm - garbage in port: \"%s\"", garbage); |
| return -EINVAL; |
| } |
| } |
| |
| return 0; |
| } |
| #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", |
| ((unsigned char *)binaddr)[0], |
| ((unsigned char *)binaddr)[1], |
| ((unsigned char *)binaddr)[2], |
| ((unsigned char *)binaddr)[3])) |
| >= textlen) { |
| errno = ENOSPC; return NULL; |
| } |
| break; |
| #if WITH_IP6 |
| case PF_INET6: |
| if ((retlen = |
| xio_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]) |
| )) |
| >= textlen) { |
| errno = ENOSPC; return NULL; |
| } |
| 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 (xio_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)) >= blen) { |
| Warn("sockaddr_inet6_info(): buffer too short"); |
| } |
| return buff; |
| } |
| #endif /* WITH_IP6 */ |
| |
| |
| #if WITH_IP4 |
| /* Checks if the given string is an IPv4 address. |
| Returns 0 when it is an address. |
| Returns 1 when it is not an address. |
| Returns -1 when an error occurred (bad regex - internal) */ |
| int check_ip4addr( |
| const char *address) |
| { |
| #if HAVE_REGEX_H |
| regex_t preg; |
| if (regcomp(&preg, "^(25[0-5]|(2[0-4]|1[0-9]|[1-9]?)[0-9])\\.(25[0-5]|(2[0-4]|1[0-9]|[1-9]?)[0-9])\\.(25[0-5]|(2[0-4]|1[0-9]|[1-9]?)[0-9])\\.(25[0-5]|(2[0-4]|1[0-9]|[1-9]?)[0-9])$", REG_EXTENDED|REG_NOSUB) |
| != 0) { |
| return -1; /* do not handle, just state that no match */ |
| } |
| if (regexec(&preg, address, 0, NULL, 0) == 0) { |
| return 0; |
| } |
| #endif /* HAVE_REGEX_H */ |
| /* Fallback when no regexec() compiled in */ |
| return 1; |
| } |
| #endif /* WITH_IP4 */ |
| |
| #if WITH_IP6 |
| /* Checks if the given string is an IPv6 address. |
| Currently a hack, just checks a few criteria. |
| Returns 0 when it is an address. |
| Returns 1 when it is not an address. |
| Returns -1 when an error occurred (bad regex - internal) */ |
| int check_ip6addr( |
| const char *address) |
| { |
| #if HAVE_REGEX_H |
| regex_t preg; |
| if (regcomp(&preg, "^\\[[0-9a-fA-F:]*\\]$", REG_EXTENDED|REG_NOSUB) |
| != 0) { |
| return -1; /* do not handle, just state that no match */ |
| } |
| if (regexec(&preg, address, 0, NULL, 0) == 0) { |
| return 0; |
| } |
| #endif /* HAVE_REGEX_H */ |
| /* Fallback when no regexec() compiled in */ |
| return 1; |
| } |
| #endif /* WITH_IP6 */ |
| |
| /* Checks if the given string is an IPv6 address. |
| Currently a hack, just checks a few criteria. |
| Returns 0 when it is an address. |
| Returns 1 when it is not an address or on error. */ |
| int check_ipaddr( |
| const char *address) |
| { |
| int res4, res6; |
| #if WITH_IP4 |
| if ((res4 = check_ip4addr(address)) == 0) { |
| return 0; |
| } |
| #endif |
| #if WITH_IP6 |
| if ((res6 = check_ip6addr(address)) == 0) { |
| return 0; |
| } |
| #endif |
| return 1; |
| } |
| |
| |
| #if HAVE_GETGROUPLIST || (defined(HAVE_SETGRENT) && defined(HAVE_GETGRENT) && defined(HAVE_ENDGRENT)) |
| /* fills 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, 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 */ |
| |
| struct group *grp; |
| int 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; |
| #endif /* HAVE_SETGRENT... */ |
| } |
| #endif |
| |
| #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 */ |
| 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); /* ok */ |
| 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 */ |
| |
| |
| 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; |
| } |
| |
| /* 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 |
| returns 0 on success or <0 if an error occurred. */ |
| int xiosetenv(const char *varname, 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(envname); |
| for (i = 0; i < l; ++i) envname[i] = toupper((unsigned char)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 XIO_ENVNAMELEN |
| } |
| |
| int xiosetenv2(const char *varname, const char *varname2, 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); |
| for (i = 0; i < l; ++i) envname[i] = toupper((unsigned char)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((unsigned char)envname[i]); |
| return _xiosetenv(envname, value, overwrite, sep); |
| # 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); |
| # 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); |
| # undef XIO_SHORTLEN |
| } |
| |
| |
| unsigned long int Strtoul(const char *nptr, char **endptr, int base, const char *txt) { |
| unsigned long res; |
| |
| res = strtoul(nptr, endptr, base); |
| if (nptr == *endptr) { |
| Error1("parseopts(): missing numerical value of option \"%s\"", txt); |
| } |
| if (**endptr != '\0') { |
| Error1("parseopts(): trailing garbage in numerical arg of option \"%s\"", txt); |
| } |
| return res; |
| } |
| |
| #if HAVE_STRTOLL |
| long long int Strtoll(const char *nptr, char **endptr, int base, const char *txt) { |
| long long int res; |
| |
| res = strtoul(nptr, endptr, base); |
| if (nptr == *endptr) { |
| Error1("parseopts(): missing numerical value of option \"%s\"", txt); |
| } |
| if (**endptr != '\0') { |
| Error1("parseopts(): trailing garbage in numerical arg of option \"%s\"", txt); |
| } |
| return res; |
| } |
| #endif /* HAVE_STRTOLL */ |
| |
| double Strtod(const char *nptr, char **endptr, const char *txt) { |
| double res; |
| |
| res = strtod(nptr, endptr); |
| if (nptr == *endptr) { |
| Error1("parseopts(): missing numerical value of option \"%s\"", txt); |
| } |
| if (**endptr != '\0') { |
| Error1("parseopts(): trailing garbage in numerical arg of option \"%s\"", txt); |
| } |
| return res; |
| } |
| |
| |
| /* This function gets a string with possible references to environment |
| variables and expands them. Variables must be prefixed with '$' and their |
| names consist only of A-Z, a-z, 0-9, and '_'. |
| The name may be enclosed with { }. |
| To pass a literal "$" us "\$". |
| There are special variables supported whose values do not comr from |
| environment: |
| $$ expands to the process's pid |
| $PROGNAME expands to the executables basename or the value of option -lp |
| $TIMESTAMP expands to the actual time in format %Y%m%dT%H%M%S |
| $MICROS expands to the actual microseconds (decimal) |
| The dst output is a copy of src but with the variables expanded. |
| Returns 0 on success; |
| returns -1 when the output buffer was too short (overflow); |
| returns 1 on syntax error. |
| */ |
| int expandenv( |
| char *dst, /* prealloc'd output buff, will be \0 termd */ |
| const char *src, /* input string to generate expansion from */ |
| size_t n, /* length of dst */ |
| struct timeval *tv) /* in/out timestamp for sync */ |
| { |
| char c; /* char currently being lex'd/parsed */ |
| bool esc = false; /* just got '\' */ |
| bool bra = false; /* within ${ } */ |
| char *nam = NULL; /* points to temp.allocated rw copy of src */ |
| const char *e; /* pointer to isolated var name in tmp[] */ |
| size_t s=0, d=0; /* counters in src, dst */ |
| size_t v; /* variable name begin */ |
| bool ofl = false; /* dst overflow, output truncated */ |
| char tmp[18]; /* buffer for timestamp, micros */ |
| |
| while (c = src[s++]) { |
| if (esc) { |
| if (c == '\0') { |
| if (d+2 > n) { ofl = true; break; } |
| dst[d++] = '\\'; |
| dst[d++] = c; |
| break; |
| } |
| if (c != '$') { |
| if (d+3 > n) { ofl = true; break; } |
| dst[d++] = '\\'; |
| } else { |
| if (d+2 > n) { ofl = true; break; } |
| } |
| dst[d++] = c; |
| esc = false; |
| continue; |
| } |
| if (c == '\0') { |
| dst[d++] = c; |
| break; |
| } |
| if (c == '\\') { |
| esc = true; |
| continue; |
| } |
| if (c != '$') { |
| if (d+2 > n) { ofl = true; break; } |
| dst[d++] = c; |
| continue; |
| } |
| |
| /* c == '$': Expecting env var to expand */ |
| c = src[s]; |
| if (c == '\0') { |
| if (d+2 > n) { ofl = true; break; } |
| ++s; |
| dst[d++] = '$'; |
| break; |
| } |
| if (c == '$') { |
| int wr; |
| /* Special case: pid */ |
| ++s; |
| wr = snprintf(&dst[d], n-d, F_pid, getpid()); |
| if (wr >= n-d || wr < 0) { ofl = true; break; } |
| d += wr; |
| continue; |
| } |
| if (c == '{') { |
| ++s; |
| bra = true; |
| } |
| if (!isalpha(c) && c != '_') { |
| /* Special case no var name, just keep '$' */ |
| if (d+3 > n) { ofl = true; break; } |
| ++s; |
| dst[d++] = '$'; |
| dst[d++] = c; |
| continue; |
| } |
| v = 0; /* seems we found valid variable name */ |
| if (nam == NULL) { |
| nam = strdup(src+s); |
| if (nam == NULL) { |
| errno = ENOMEM; |
| return -1; |
| } |
| } |
| while (c = src[s]) { |
| if (!isalnum(c) && c != '_') { |
| break; |
| } |
| nam[v++] = c; |
| ++s; |
| } |
| if (bra && c != '}') { |
| return 1; |
| } |
| |
| nam[v] = '\0'; |
| /* Var name is complete */ |
| /* Check hardcoded var names */ |
| if (strcmp(nam, "PROGNAME") == 0) { |
| e = diag_get_string('p'); |
| } else if (strcmp(nam, "TIMESTAMP") == 0) { |
| if (tv->tv_sec == 0) { |
| gettimeofday(tv, NULL); |
| } |
| struct tm tm; |
| localtime_r(&tv->tv_sec, &tm); |
| strftime(tmp, sizeof(tmp), "%Y%m%dT%H%M%S", &tm); |
| e = tmp; |
| } else if (strcmp(nam, "MICROS") == 0) { |
| if (tv->tv_sec == 0) { |
| gettimeofday(tv, NULL); |
| } |
| sprintf(tmp, F_tv_usec, tv->tv_usec); |
| e = tmp; |
| } else { |
| e = getenv(nam); |
| } |
| if (e == NULL) { |
| /* Var not found, skip it */ |
| continue; |
| } |
| /* Var found, copy it to output buffer */ |
| if (d+strlen(e)+1 > n) { ofl = true; break; } |
| strcpy(&dst[d], e); |
| d += strlen(&dst[d]); |
| continue; |
| } |
| |
| if (nam != NULL) { |
| free(nam); |
| } |
| |
| if (ofl) { |
| dst[d] = '\0'; |
| return -1; |
| } |
| dst[d] = '\0'; |
| if (src[s-1] != '\0') { |
| errno = EINVAL; |
| return -1; |
| } |
| return 0; |
| } |
| |
| int xio_opensnifffile( |
| const char *a, |
| struct timeval *tv) |
| { |
| char path[PATH_MAX]; |
| int flags; |
| int rc; |
| int fd; |
| |
| rc = expandenv(path, a, sizeof(path), tv); |
| if (rc < 0) { |
| Error2("expandenv(source=\"%s\", n="F_Zu"): Out of memory", a, sizeof(path)); |
| errno = ENOMEM; |
| return -1; |
| } else if (rc > 0) { |
| Error1("expandenv(source=\"%s\"): Syntax error", a); |
| errno = EINVAL; |
| return -1; |
| } |
| flags = O_CREAT|O_WRONLY|O_APPEND| |
| #ifdef O_LARGEFILE |
| O_LARGEFILE| |
| #endif |
| #ifdef O_CLOEXEC |
| O_CLOEXEC| |
| #endif |
| O_NONBLOCK; |
| if ((fd = Open(path, flags, 0664)) < 0) { |
| if (errno == ENXIO) { |
| /* try to open pipe rdwr */ |
| if ((fd = Open(path, flags, 0664)) < 0) { |
| return -1; |
| } |
| } |
| } |
| #ifdef O_CLOEXEC |
| if (Fcntl_l(fd, F_SETFD, FD_CLOEXEC) < 0) { |
| Warn2("fcntl(%d, F_SETFD, FD_CLOEXEC): %s", fd, strerror(errno)); |
| } |
| #endif |
| return fd; |
| } |