/* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 1998-2021 The OpenLDAP Foundation. * Portions Copyright 1998 A. Hartgers. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* ACKNOWLEDGEMENTS: * This work was initially developed by Bart Hartgers for inclusion in * OpenLDAP Software. */ /* * util-int.c Various functions to replace missing threadsafe ones. * Without the real *_r funcs, things will * work, but might not be threadsafe. */ #include "portable.h" #include #include #include #include #include #include #include "ldap-int.h" #ifndef h_errno /* newer systems declare this in for you, older ones don't. * harmless to declare it again (unless defined by a macro). */ extern int h_errno; #endif #ifdef HAVE_HSTRERROR # define HSTRERROR(e) hstrerror(e) #else # define HSTRERROR(e) hp_strerror(e) #endif #ifndef LDAP_R_COMPILE # undef HAVE_REENTRANT_FUNCTIONS # undef HAVE_CTIME_R # undef HAVE_GETHOSTBYNAME_R # undef HAVE_GETHOSTBYADDR_R #else # include ldap_pvt_thread_mutex_t ldap_int_resolv_mutex; ldap_pvt_thread_mutex_t ldap_int_hostname_mutex; static ldap_pvt_thread_mutex_t ldap_int_gettime_mutex; # if (defined( HAVE_CTIME_R ) || defined( HAVE_REENTRANT_FUNCTIONS)) \ && defined( CTIME_R_NARGS ) # define USE_CTIME_R # else static ldap_pvt_thread_mutex_t ldap_int_ctime_mutex; # endif /* USE_GMTIME_R and USE_LOCALTIME_R defined in ldap_pvt.h */ #ifdef LDAP_DEVEL /* to be released with 2.5 */ #if !defined( USE_GMTIME_R ) || !defined( USE_LOCALTIME_R ) /* we use the same mutex for gmtime(3) and localtime(3) * because implementations may use the same buffer * for both functions */ static ldap_pvt_thread_mutex_t ldap_int_gmtime_mutex; #endif #else /* ! LDAP_DEVEL */ ldap_pvt_thread_mutex_t ldap_int_gmtime_mutex; #endif /* ! LDAP_DEVEL */ # if defined(HAVE_GETHOSTBYNAME_R) && \ (GETHOSTBYNAME_R_NARGS < 5) || (6 < GETHOSTBYNAME_R_NARGS) /* Don't know how to handle this version, pretend it's not there */ # undef HAVE_GETHOSTBYNAME_R # endif # if defined(HAVE_GETHOSTBYADDR_R) && \ (GETHOSTBYADDR_R_NARGS < 7) || (8 < GETHOSTBYADDR_R_NARGS) /* Don't know how to handle this version, pretend it's not there */ # undef HAVE_GETHOSTBYADDR_R # endif #endif /* LDAP_R_COMPILE */ char *ldap_pvt_ctime( const time_t *tp, char *buf ) { #ifdef USE_CTIME_R # if (CTIME_R_NARGS > 3) || (CTIME_R_NARGS < 2) # error "CTIME_R_NARGS should be 2 or 3" # elif CTIME_R_NARGS > 2 && defined(CTIME_R_RETURNS_INT) return( ctime_r(tp,buf,26) < 0 ? 0 : buf ); # elif CTIME_R_NARGS > 2 return ctime_r(tp,buf,26); # else return ctime_r(tp,buf); # endif #else LDAP_MUTEX_LOCK( &ldap_int_ctime_mutex ); AC_MEMCPY( buf, ctime(tp), 26 ); LDAP_MUTEX_UNLOCK( &ldap_int_ctime_mutex ); return buf; #endif } #if !defined( USE_GMTIME_R ) || !defined( USE_LOCALTIME_R ) int ldap_pvt_gmtime_lock( void ) { # ifndef LDAP_R_COMPILE return 0; # else /* LDAP_R_COMPILE */ return ldap_pvt_thread_mutex_lock( &ldap_int_gmtime_mutex ); # endif /* LDAP_R_COMPILE */ } int ldap_pvt_gmtime_unlock( void ) { # ifndef LDAP_R_COMPILE return 0; # else /* LDAP_R_COMPILE */ return ldap_pvt_thread_mutex_unlock( &ldap_int_gmtime_mutex ); # endif /* LDAP_R_COMPILE */ } #endif /* !USE_GMTIME_R || !USE_LOCALTIME_R */ #ifndef USE_GMTIME_R struct tm * ldap_pvt_gmtime( const time_t *timep, struct tm *result ) { struct tm *tm_ptr; LDAP_MUTEX_LOCK( &ldap_int_gmtime_mutex ); tm_ptr = gmtime( timep ); if ( tm_ptr == NULL ) { result = NULL; } else { *result = *tm_ptr; } LDAP_MUTEX_UNLOCK( &ldap_int_gmtime_mutex ); return result; } #endif /* !USE_GMTIME_R */ #ifndef USE_LOCALTIME_R struct tm * ldap_pvt_localtime( const time_t *timep, struct tm *result ) { struct tm *tm_ptr; LDAP_MUTEX_LOCK( &ldap_int_gmtime_mutex ); tm_ptr = localtime( timep ); if ( tm_ptr == NULL ) { result = NULL; } else { *result = *tm_ptr; } LDAP_MUTEX_UNLOCK( &ldap_int_gmtime_mutex ); return result; } #endif /* !USE_LOCALTIME_R */ static int _ldap_pvt_gt_subs; #ifdef _WIN32 /* Windows SYSTEMTIME only has 10 millisecond resolution, so we * also need to use a high resolution timer to get microseconds. * This is pretty clunky. */ static LARGE_INTEGER _ldap_pvt_gt_freq; static LARGE_INTEGER _ldap_pvt_gt_prev; static int _ldap_pvt_gt_offset; #define SEC_TO_UNIX_EPOCH 11644473600LL #define TICKS_PER_SECOND 10000000 static int ldap_pvt_gettimeusec(int *sec) { LARGE_INTEGER count; QueryPerformanceCounter( &count ); /* It shouldn't ever go backwards, but multiple CPUs might * be able to hit in the same tick. */ LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex ); /* We assume Windows has at least a vague idea of * when a second begins. So we align our microsecond count * with the Windows millisecond count using this offset. * We retain the submillisecond portion of our own count. * * Note - this also assumes that the relationship between * the PerformanceCounter and SystemTime stays constant; * that assumption breaks if the SystemTime is adjusted by * an external action. */ if ( !_ldap_pvt_gt_freq.QuadPart ) { LARGE_INTEGER c2; ULARGE_INTEGER ut; FILETIME ft0, ft1; long long t; int usec; /* Initialize our offset */ QueryPerformanceFrequency( &_ldap_pvt_gt_freq ); /* Wait for a tick of the system time: 10-15ms */ GetSystemTimeAsFileTime( &ft0 ); do { GetSystemTimeAsFileTime( &ft1 ); } while ( ft1.dwLowDateTime == ft0.dwLowDateTime ); ut.LowPart = ft1.dwLowDateTime; ut.HighPart = ft1.dwHighDateTime; QueryPerformanceCounter( &c2 ); /* get second and fraction portion of counter */ t = c2.QuadPart % (_ldap_pvt_gt_freq.QuadPart*10); /* convert to microseconds */ t *= 1000000; usec = t / _ldap_pvt_gt_freq.QuadPart; ut.QuadPart /= 10; ut.QuadPart %= 10000000; _ldap_pvt_gt_offset = usec - ut.QuadPart; count = c2; } if ( count.QuadPart <= _ldap_pvt_gt_prev.QuadPart ) { _ldap_pvt_gt_subs++; } else { _ldap_pvt_gt_subs = 0; _ldap_pvt_gt_prev = count; } LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex ); /* convert to microseconds */ count.QuadPart %= _ldap_pvt_gt_freq.QuadPart*10; count.QuadPart *= 1000000; count.QuadPart /= _ldap_pvt_gt_freq.QuadPart; count.QuadPart -= _ldap_pvt_gt_offset; /* We've extracted the 1s and microseconds. * The 1sec digit is used to detect wraparound in microsecnds. */ if (count.QuadPart < 0) count.QuadPart += 10000000; else if (count.QuadPart >= 10000000) count.QuadPart -= 10000000; *sec = count.QuadPart / 1000000; return count.QuadPart % 1000000; } /* emulate POSIX gettimeofday */ int ldap_pvt_gettimeofday( struct timeval *tv, void *unused ) { FILETIME ft; ULARGE_INTEGER ut; int sec, sec0; GetSystemTimeAsFileTime( &ft ); ut.LowPart = ft.dwLowDateTime; ut.HighPart = ft.dwHighDateTime; /* convert to usec */ ut.QuadPart /= (TICKS_PER_SECOND / 1000000); tv->tv_usec = ldap_pvt_gettimeusec(&sec); tv->tv_sec = ut.QuadPart / 1000000 - SEC_TO_UNIX_EPOCH; /* check for carry from microseconds */ sec0 = tv->tv_sec % 10; if (sec0 < sec || (sec0 == 9 && !sec)) tv->tv_sec++; return 0; } /* return a broken out time, with microseconds */ void ldap_pvt_gettime( struct lutil_tm *tm ) { SYSTEMTIME st; int sec, sec0; static const char daysPerMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; GetSystemTime( &st ); tm->tm_usec = ldap_pvt_gettimeusec(&sec); tm->tm_usub = _ldap_pvt_gt_subs; /* any difference larger than microseconds is * already reflected in st */ tm->tm_sec = st.wSecond; tm->tm_min = st.wMinute; tm->tm_hour = st.wHour; tm->tm_mday = st.wDay; tm->tm_mon = st.wMonth - 1; tm->tm_year = st.wYear - 1900; /* check for carry from microseconds */ sec0 = tm->tm_sec % 10; if (sec0 < sec || (sec0 == 9 && !sec)) { tm->tm_sec++; /* FIXME: we don't handle leap seconds */ if (tm->tm_sec > 59) { tm->tm_sec = 0; tm->tm_min++; if (tm->tm_min > 59) { tm->tm_min = 0; tm->tm_hour++; if (tm->tm_hour > 23) { int days = daysPerMonth[tm->tm_mon]; tm->tm_hour = 0; tm->tm_mday++; /* if it's February of a leap year, * add 1 day to this month */ if (tm->tm_mon == 1 && ((!(st.wYear % 4) && (st.wYear % 100)) || !(st.wYear % 400))) days++; if (tm->tm_mday > days) { tm->tm_mday = 1; tm->tm_mon++; if (tm->tm_mon > 11) { tm->tm_mon = 0; tm->tm_year++; } } } } } } } #else static struct timeval _ldap_pvt_gt_prevTv; void ldap_pvt_gettime( struct lutil_tm *ltm ) { struct timeval tv; struct tm tm; time_t t; gettimeofday( &tv, NULL ); t = tv.tv_sec; LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex ); if ( tv.tv_sec < _ldap_pvt_gt_prevTv.tv_sec || ( tv.tv_sec == _ldap_pvt_gt_prevTv.tv_sec && tv.tv_usec <= _ldap_pvt_gt_prevTv.tv_usec )) { _ldap_pvt_gt_subs++; } else { _ldap_pvt_gt_subs = 0; _ldap_pvt_gt_prevTv = tv; } LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex ); ltm->tm_usub = _ldap_pvt_gt_subs; ldap_pvt_gmtime( &t, &tm ); ltm->tm_sec = tm.tm_sec; ltm->tm_min = tm.tm_min; ltm->tm_hour = tm.tm_hour; ltm->tm_mday = tm.tm_mday; ltm->tm_mon = tm.tm_mon; ltm->tm_year = tm.tm_year; ltm->tm_usec = tv.tv_usec; } #endif size_t ldap_pvt_csnstr(char *buf, size_t len, unsigned int replica, unsigned int mod) { struct lutil_tm tm; int n; ldap_pvt_gettime( &tm ); n = snprintf( buf, len, "%4d%02d%02d%02d%02d%02d.%06dZ#%06x#%03x#%06x", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_usec, tm.tm_usub, replica, mod ); if( n < 0 ) return 0; return ( (size_t) n < len ) ? n : 0; } #define BUFSTART (1024-32) #define BUFMAX (32*1024-32) #if defined(LDAP_R_COMPILE) static char *safe_realloc( char **buf, int len ); #if !(defined(HAVE_GETHOSTBYNAME_R) && defined(HAVE_GETHOSTBYADDR_R)) static int copy_hostent( struct hostent *res, char **buf, struct hostent * src ); #endif #endif int ldap_pvt_gethostbyname_a( const char *name, struct hostent *resbuf, char **buf, struct hostent **result, int *herrno_ptr ) { #if defined( HAVE_GETHOSTBYNAME_R ) # define NEED_SAFE_REALLOC 1 int r=-1; int buflen=BUFSTART; *buf = NULL; for(;buflensa_family == AF_INET6) { struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa; addr = (char *)&sin->sin6_addr; alen = sizeof(sin->sin6_addr); } else #endif if (sa->sa_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)sa; addr = (char *)&sin->sin_addr; alen = sizeof(sin->sin_addr); } else { rc = NO_RECOVERY; *err = (char *)HSTRERROR( rc ); return rc; } #if defined( HAVE_GETHOSTBYADDR_R ) for(;buflensa_family, &hb, buf, buflen, &h_errno ); rc = (hp == NULL) ? -1 : 0; #else rc = gethostbyaddr_r( addr, alen, sa->sa_family, &hb, buf, buflen, &hp, &h_errno ); #endif #ifdef NETDB_INTERNAL if ((rc<0) && (h_errno==NETDB_INTERNAL) && (errno==ERANGE)) { buflen*=2; continue; } #endif break; } if (hp) { strncpy( name, hp->h_name, namelen ); } else { *err = (char *)HSTRERROR( h_errno ); } LDAP_FREE(buf); #else /* HAVE_GETHOSTBYADDR_R */ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); hp = gethostbyaddr( addr, alen, sa->sa_family ); if (hp) { strncpy( name, hp->h_name, namelen ); rc = 0; } else { rc = h_errno; *err = (char *)HSTRERROR( h_errno ); } LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); #endif /* !HAVE_GETHOSTBYADDR_R */ return rc; #endif /* !HAVE_GETNAMEINFO */ } int ldap_pvt_gethostbyaddr_a( const char *addr, int len, int type, struct hostent *resbuf, char **buf, struct hostent **result, int *herrno_ptr ) { #if defined( HAVE_GETHOSTBYADDR_R ) # undef NEED_SAFE_REALLOC # define NEED_SAFE_REALLOC int r=-1; int buflen=BUFSTART; *buf = NULL; for(;buflenh_name ) + 1; if( src->h_aliases != NULL ) { for( p = src->h_aliases; (*p) != NULL; p++ ) { total_alias_len += strlen( *p ) + 1; n_alias++; } } if( src->h_addr_list != NULL ) { for( p = src->h_addr_list; (*p) != NULL; p++ ) { n_addr++; } total_addr_len = n_addr * src->h_length; } total_len = (n_alias + n_addr + 2) * sizeof( char * ) + total_addr_len + total_alias_len + name_len; if (safe_realloc( buf, total_len )) { tp = (char **) *buf; tbuf = *buf + (n_alias + n_addr + 2) * sizeof( char * ); AC_MEMCPY( res, src, sizeof( struct hostent ) ); /* first the name... */ AC_MEMCPY( tbuf, src->h_name, name_len ); res->h_name = tbuf; tbuf+=name_len; /* now the aliases */ res->h_aliases = tp; if ( src->h_aliases != NULL ) { tbuf = cpy_aliases( &tp, tbuf, src->h_aliases ); } *tp++=NULL; /* finally the addresses */ res->h_addr_list = tp; if ( src->h_addr_list != NULL ) { tbuf = cpy_addresses( &tp, tbuf, src->h_addr_list, src->h_length ); } *tp++=NULL; return 0; } return -1; } #endif #if defined( NEED_SAFE_REALLOC ) static char *safe_realloc( char **buf, int len ) { char *tmpbuf; tmpbuf = LDAP_REALLOC( *buf, len ); if (tmpbuf) { *buf=tmpbuf; } return tmpbuf; } #endif char * ldap_pvt_get_fqdn( char *name ) { #ifdef HAVE_GETADDRINFO struct addrinfo hints, *res; #else char *ha_buf; struct hostent *hp, he_buf; int local_h_errno; #endif int rc; char *fqdn, hostbuf[MAXHOSTNAMELEN+1]; if( name == NULL ) { if( gethostname( hostbuf, MAXHOSTNAMELEN ) == 0 ) { hostbuf[MAXHOSTNAMELEN] = '\0'; name = hostbuf; } else { name = "localhost"; } } #ifdef HAVE_GETADDRINFO memset( &hints, 0, sizeof( hints )); hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_CANONNAME; LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); rc = getaddrinfo( name, NULL, &hints, &res ); LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); if ( rc == 0 && res->ai_canonname ) { fqdn = LDAP_STRDUP( res->ai_canonname ); } else { fqdn = LDAP_STRDUP( name ); } if ( rc == 0 ) freeaddrinfo( res ); #else rc = ldap_pvt_gethostbyname_a( name, &he_buf, &ha_buf, &hp, &local_h_errno ); if( rc < 0 || hp == NULL || hp->h_name == NULL ) { fqdn = LDAP_STRDUP( name ); } else { fqdn = LDAP_STRDUP( hp->h_name ); } LDAP_FREE( ha_buf ); #endif return fqdn; } #if ( defined( HAVE_GETADDRINFO ) || defined( HAVE_GETNAMEINFO ) ) \ && !defined( HAVE_GAI_STRERROR ) char *ldap_pvt_gai_strerror (int code) { static struct { int code; const char *msg; } values[] = { #ifdef EAI_ADDRFAMILY { EAI_ADDRFAMILY, N_("Address family for hostname not supported") }, #endif { EAI_AGAIN, N_("Temporary failure in name resolution") }, { EAI_BADFLAGS, N_("Bad value for ai_flags") }, { EAI_FAIL, N_("Non-recoverable failure in name resolution") }, { EAI_FAMILY, N_("ai_family not supported") }, { EAI_MEMORY, N_("Memory allocation failure") }, #ifdef EAI_NODATA { EAI_NODATA, N_("No address associated with hostname") }, #endif { EAI_NONAME, N_("Name or service not known") }, { EAI_SERVICE, N_("Servname not supported for ai_socktype") }, { EAI_SOCKTYPE, N_("ai_socktype not supported") }, #ifdef EAI_SYSTEM { EAI_SYSTEM, N_("System error") }, #endif { 0, NULL } }; int i; for ( i = 0; values[i].msg != NULL; i++ ) { if ( values[i].code == code ) { return (char *) _(values[i].msg); } } return _("Unknown error"); } #endif