From cf39583b253836f096aac50d91ea8984feab431b Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Sun, 26 Jan 2014 17:45:09 +0100 Subject: [PATCH] Red Hat issue 1021948: snprintf API change --- CHANGES | 5 +++++ compat.h | 32 +------------------------------- config.h.in | 3 +++ configure.in | 20 +++++++++++++++++++- sysutils.c | 39 +++++++++++++++++++++------------------ utils.c | 21 ++++++++++++++++++++- utils.h | 3 +++ xio-ip6.c | 4 ++-- 8 files changed, 74 insertions(+), 53 deletions(-) diff --git a/CHANGES b/CHANGES index 03d1cb5..8f32ad6 100644 --- a/CHANGES +++ b/CHANGES @@ -62,6 +62,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 + docu: libwrap always logs to syslog diff --git a/compat.h b/compat.h index d581fad..f8847f6 100644 --- a/compat.h +++ b/compat.h @@ -1,5 +1,5 @@ /* source: compat.h */ -/* Copyright Gerhard Rieger 2001-2011 */ +/* 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 f03ed3a..0f805d4 100644 --- a/config.h.in +++ b/config.h.in @@ -439,6 +439,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 c94397f..942b45b 100644 --- a/configure.in +++ b/configure.in @@ -1284,11 +1284,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(void){ char s[16]; sprintf(s,"%Zu",1); exit(strcmp(s,"1")); diff --git a/sysutils.c b/sysutils.c index fb39d1a..6ef2fc8 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; } 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 */ + >= textlen) { + errno = ENOSPC; return NULL; } 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 df960c8..2b13862 100644 --- a/utils.c +++ b/utils.c @@ -1,5 +1,5 @@ /* source: utils.c */ -/* Copyright Gerhard Rieger 2001-2009 */ +/* Copyright Gerhard Rieger */ /* Published under the GNU General Public License V.2, see file COPYING */ /* useful additions to C library */ @@ -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..d2f2001 100644 --- a/utils.h +++ b/utils.h @@ -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 60218ac..23287a2 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'; }