diff --git a/CHANGES b/CHANGES index 5cd576b..f207aae 100644 --- a/CHANGES +++ b/CHANGES @@ -152,6 +152,11 @@ porting: use getgrouplist() when available instead of sequence of calls to getgrent() + Red Hat issue 1021948: snprintf API change; + Implemented xio_snprintf() function as wrapper that tries to emulate C99 + behaviour on old glibc systems, and adapted all affected calls + appropriately + libwrap always logs to syslog added actual text version of GPLv2 diff --git a/compat.h b/compat.h index f5970fa..fc349b9 100644 --- a/compat.h +++ b/compat.h @@ -1,5 +1,5 @@ /* source: compat.h */ -/* Copyright Gerhard Rieger 2001-2012 */ +/* Copyright Gerhard Rieger */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __compat_h_included @@ -622,34 +622,4 @@ extern const char *hstrerror(int); #endif -/*****************************************************************************/ -/* here are the declarations of compat.c */ - -#if !HAVE_SIGACTION -struct sigaction { - void (*sa_handler)(int); - void (*sa_sigaction)(int, siginfo_t *, void *); - sigset_t sa_mask; - int sa_flags; -} ; -struct siginfo { - int si_signo; - int si_errno; - int si_code; - pid_t si_pid; - uid_t si_uid; - int si_status; - /*clock_t si_utime;*/ - /*clock_t si_stime;*/ - sigval_t si_value; - int si_int; - void *si_ptr; - void *si_addr; - /*int si_band;*/ - /*int si_fd;*/ -} ; -extern int sigaction(int signum, const struct sigaction *act, - struct sigaction *oldact); -#endif /* !HAVE_SIGACTION */ - #endif /* !defined(__compat_h_included) */ diff --git a/config.h.in b/config.h.in index ee29666..1cf5a5a 100644 --- a/config.h.in +++ b/config.h.in @@ -438,6 +438,9 @@ /* is uint64_t already defined? */ #undef HAVE_TYPE_UINT64 + +/* Define if snprintf() returns required len on truncation (C-99 conform) */ +#undef HAVE_C99_SNPRINTF /* Define if you have the printf "Z" modifier */ #undef HAVE_FORMAT_Z diff --git a/configure.in b/configure.in index cd08395..70fbae0 100644 --- a/configure.in +++ b/configure.in @@ -1300,11 +1300,29 @@ AC_CHECK_FUNC(SSLv2_server_method, AC_DEFINE(HAVE_SSLv2_server_method), AC_CHECK dnl Run time checks +AC_MSG_CHECKING(if snprintf conforms to C99) +AC_CACHE_VAL(ac_cv_have_c99_snprintf, +[AC_TRY_RUN([ +#include +#include +int main(void){ +char s[2]; +exit(snprintf(s,2,"ab")!=2); +}], +[ac_cv_have_c99_snprintf=yes], +[ac_cv_have_c99_snprintf=no], +[ac_cv_have_c99_snprintf=no])]) +if test $ac_cv_have_c99_snprintf = yes; then + AC_DEFINE(HAVE_C99_SNPRINTF) +fi +AC_MSG_RESULT($ac_cv_have_c99_snprintf) + + AC_MSG_CHECKING(if printf has Z modifier) AC_CACHE_VAL(ac_cv_have_z_modifier, [AC_TRY_RUN([ #include -main(){ +int main(){ char s[16]; sprintf(s,"%Zu",1); exit(strcmp(s,"1")); diff --git a/sysutils.c b/sysutils.c index 44eea9f..3d1545b 100644 --- a/sysutils.c +++ b/sysutils.c @@ -173,14 +173,16 @@ char *sockaddr_info(const struct sockaddr *sa, socklen_t salen, char *buff, size int n; #if HAVE_STRUCT_SOCKADDR_SALEN - if ((n = snprintf(cp, blen, "LEN=%d ", sau->soa.sa_len)) < 0) { + 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 - if ((n = snprintf(cp, blen, "AF=%d ", sau->soa.sa_family)) < 0) { + 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; @@ -204,13 +206,14 @@ char *sockaddr_info(const struct sockaddr *sa, socklen_t salen, char *buff, size break; #endif default: - if ((n = snprintf(cp, blen, "AF=%d ", sa->sa_family)) < 0) { + 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; - if ((snprintf(cp, blen, + 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], @@ -225,7 +228,8 @@ char *sockaddr_info(const struct sockaddr *sa, socklen_t salen, char *buff, size ((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) { + ((unsigned char *)sau->soa.sa_data)[13]); + if (n < 0 || n >= blen) { Warn("sockaddr_info(): buffer too short"); *buff = '\0'; return buff; @@ -268,9 +272,9 @@ char *sockaddr_unix_info(const struct sockaddr_un *sa, socklen_t salen, char *bu #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", + 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)) < 0) { + (unsigned int)((addr >> 8) & 0xff), (unsigned int)(addr & 0xff)) >= blen) { Warn("inet4addr_info(): buffer too short"); buff[blen-1] = '\0'; } @@ -280,12 +284,12 @@ char *inet4addr_info(uint32_t addr, char *buff, size_t blen) { #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", + 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)) < 0) { + htons(sa->sin_port)) >= blen) { Warn("sockaddr_inet4_info(): buffer too short"); buff[blen-1] = '\0'; } @@ -301,19 +305,19 @@ const char *inet_ntop(int pf, const void *binaddr, switch (pf) { case PF_INET: if ((retlen = - snprintf(addrtext, textlen, "%u.%u.%u.%u", + xio_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 */ + >= textlen) { + errno = ENOSPC; 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", + 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]), @@ -323,8 +327,8 @@ const char *inet_ntop(int pf, const void *binaddr, ntohs(((uint16_t *)binaddr)[6]), ntohs(((uint16_t *)binaddr)[7]) )) - < 0) { - return NULL; /* errno is valid */ + >= 0 textlen) { + errno = ENOSPC; return NULL; /* errno is valid */ } break; #endif /* WITH_IP6 */ @@ -341,7 +345,7 @@ const char *inet_ntop(int pf, const void *binaddr, /* 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 (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], @@ -405,9 +409,8 @@ char *sockaddr_inet6_info(const struct sockaddr_in6 *sa, char *buff, size_t blen 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) { + ntohs(sa->sin6_port)) >= blen) { Warn("sockaddr_inet6_info(): buffer too short"); - buff[blen-1] = '\0'; } return buff; } diff --git a/utils.c b/utils.c index a08ba16..263b22f 100644 --- a/utils.c +++ b/utils.c @@ -161,3 +161,22 @@ char *xiosubstr(char *scratch, const char *str, size_t from, size_t len) { return scratch0; } + +/* since version 1.7.2.4 socat supports C-99 behaviour of snprintf but still + can handle the old glibc case with -1 return on truncation. + Do not rely on exact return value in case of truncation + */ +int xio_snprintf(char *str, size_t size, const char *format, ...) { + va_list ap; + int result; + + va_start(ap, format); + result = vsnprintf(str, size, format, ap); +#if ! HAVE_C99_SNPRINTF + if (result < 0) { + result = size+63; /* indicate truncation with just some guess */ + } +#endif /* !HAVE_C99_SNPRINTF */ + va_end(ap); + return result; +} diff --git a/utils.h b/utils.h index f942db1..a5c24a5 100644 --- a/utils.h +++ b/utils.h @@ -1,5 +1,5 @@ /* source: utils.h */ -/* Copyright Gerhard Rieger 2001-2008 */ +/* Copyright Gerhard Rieger */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __utils_h_included @@ -66,4 +66,7 @@ char *sanitize_string(const char *data, /* input data */ extern char *xiosubstr(char *scratch, const char *str, size_t from, size_t len); +extern +int xio_snprintf(char *str, size_t size, const char *format, ...); + #endif /* !defined(__utils_h_included) */ diff --git a/xio-ip6.c b/xio-ip6.c index 6fd5507..9721d61 100644 --- a/xio-ip6.c +++ b/xio-ip6.c @@ -312,7 +312,7 @@ int xiolog_ancillary_ip6(struct cmsghdr *cmsg, int *num, /* convert the IP6 socket address to human readable form. buff should be at least 50 chars long. output includes the port number */ static char *inet6addr_info(const struct in6_addr *sa, char *buff, size_t blen) { - if (snprintf(buff, blen, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]", + if (xio_snprintf(buff, blen, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]", #if HAVE_IP6_SOCKADDR==0 (sa->s6_addr[0]<<8)+sa->s6_addr[1], (sa->s6_addr[2]<<8)+sa->s6_addr[3], @@ -368,7 +368,7 @@ static char *inet6addr_info(const struct in6_addr *sa, char *buff, size_t blen) ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[6]), ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[7]) #endif - ) < 0) { + ) >= blen) { Warn("sockaddr_inet6_info(): buffer too short"); buff[blen-1] = '\0'; }