/*
* Copyright (c) Likewise Software. All rights Reserved.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the license, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy
* of the GNU Lesser General Public License along with this program. If
* not, see .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewisesoftware.com
*/
/*
* Module Name:
*
* util.c
*
* Abstract:
*
* Utility functions
*
* Authors: Brian Koropoff (bkoropoff@likewisesoftware.com)
*
*/
#include
#include "util-private.h"
#include "xnet-private.h"
#ifdef HAVE_SYS_SELECT_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if HAVE_SYS_VARARGS_H
#include
#endif
#include
#include
char* lwmsg_formatv(const char* fmt, va_list ap)
{
int len;
va_list my_ap;
char* str, *str_new;
/* Some versions of vsnprintf won't accept
a null or zero-length buffer */
str = malloc(1);
if (!str)
{
return NULL;
}
va_copy(my_ap, ap);
len = vsnprintf(str, 1, fmt, my_ap);
/* Some versions of vsnprintf return -1 when
the buffer was too small rather than the
number of characters that would be written,
so we have loop in search of a large enough
buffer */
if (len == -1)
{
int capacity = 16;
do
{
capacity *= 2;
va_copy(my_ap, ap);
str_new = realloc(str, capacity);
if (!str_new)
{
free(str);
return NULL;
}
str = str_new;
} while ((len = vsnprintf(str, capacity, fmt, my_ap)) == -1 || capacity <= len);
str[len] = '\0';
return str;
}
else
{
va_copy(my_ap, ap);
str_new = realloc(str, len+1);
if (!str_new)
{
free(str);
return NULL;
}
str = str_new;
if (vsnprintf(str, len+1, fmt, my_ap) < len)
return NULL;
else
return str;
}
}
char* lwmsg_format(const char* fmt, ...)
{
va_list ap;
char* str;
va_start(ap, fmt);
str = lwmsg_formatv(fmt, ap);
va_end(ap);
return str;
}
ssize_t
lwmsg_convert_string_alloc(
void* input,
size_t input_len,
void** output,
const char* input_type,
const char* output_type
)
{
size_t cblen;
char *buffer;
if (input == NULL)
return -1;
cblen = lwmsg_convert_string_buffer(input, input_len, NULL, 0, input_type, output_type);
buffer = malloc(cblen);
if (buffer == NULL)
return -1;
if (lwmsg_convert_string_buffer(input, input_len, buffer, cblen, input_type, output_type) != cblen)
{
free(buffer);
return -1;
}
else
{
*output = buffer;
return cblen;
}
}
ssize_t
lwmsg_convert_string_buffer(
void* input,
size_t input_len,
void* output,
size_t output_len,
const char* input_type,
const char* output_type
)
{
iconv_t handle = iconv_open(output_type, input_type);
char *inbuf = (char *)input;
char *outbuf = (char *)output;
size_t cbin = input_len;
size_t cbout = output_len;
size_t converted;
if(outbuf == NULL)
{
char buffer[100];
size_t cblen = 0;
while(cbin > 0)
{
outbuf = buffer;
cbout = sizeof(buffer);
converted = iconv(handle, (ICONV_IN_TYPE) &inbuf, &cbin, &outbuf, &cbout);
if(converted == (size_t)-1)
{
if(errno != E2BIG)
{
cblen = -1;
break;
}
}
cblen += sizeof(buffer) - cbout;
}
iconv_close(handle);
return cblen;
}
converted = iconv(handle, (ICONV_IN_TYPE) &inbuf, &cbin, &outbuf, &cbout);
iconv_close(handle);
if(converted == (size_t)-1 && cbout != 0)
return -1;
else
return output_len - cbout;
}
LWMsgStatus
lwmsg_add_unsigned(
size_t operand_a,
size_t operand_b,
size_t* result
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
size_t sum = operand_a + operand_b;
if (sum < operand_a)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_OVERFLOW);
}
*result = sum;
error:
return status;
}
#define SIZE_T_BITS (sizeof(size_t) * 8)
#define SIZE_T_HALF_BITS (SIZE_T_BITS / 2)
#define SIZE_T_LOWER_MASK (((size_t) (ssize_t) -1) >> SIZE_T_HALF_BITS)
#define SIZE_T_UPPER_MASK (~SIZE_T_LOWER_MASK)
LWMsgStatus
lwmsg_multiply_unsigned(
size_t operand_a,
size_t operand_b,
size_t* result
)
{
/* Multiplication with overflow checking:
Operands: x and y
Let r = 2^((8 * sizeof size_t) / 2)
Let x = ra + b
Let y = rc + d
Therefore xy = (ra + b) * (rc + d)
Therefore xy = (r^2)ac + rbc + rad + bd
Assume y <= x
Therefore c <= a
If c != 0
Then (r^2)ac >= r^2
Therefore xy >= r^2
Therefore raise overflow
Otherwise c = 0
Therefore xy = rad + bd
If ad >= r
Then rad >= r^2
Therefore xy >= r^2
Therefore raise overflow
Perform rad + bd with add overflow check
*/
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
size_t x;
size_t y;
size_t a;
size_t b;
size_t c;
size_t d;
size_t ad;
size_t bd;
size_t rad;
size_t product;
/* Ensure y is the smaller operand */
if (operand_b <= operand_a)
{
x = operand_a;
y = operand_b;
}
else
{
x = operand_b;
y = operand_a;
}
/* Calculate decomposition of operands */
c = y >> SIZE_T_HALF_BITS;
if (c != 0)
{
/* If c is not 0, the first term will overflow */
BAIL_ON_ERROR(status = LWMSG_STATUS_OVERFLOW);
}
a = x >> SIZE_T_HALF_BITS;
b = x & SIZE_T_LOWER_MASK;
d = y & SIZE_T_LOWER_MASK;
ad = a * d;
if ((ad & SIZE_T_UPPER_MASK) != 0)
{
/* ad >= r, so rad will overflow */
BAIL_ON_ERROR(status = LWMSG_STATUS_OVERFLOW);
}
rad = ad << SIZE_T_HALF_BITS;
/* Never overflows */
bd = b * d;
product = rad + bd;
if (product < rad)
{
/* Addition overflowed */
BAIL_ON_ERROR(status = LWMSG_STATUS_OVERFLOW);
}
*result = product;
error:
return status;
}
#ifdef HAVE_STRERROR_R
#ifdef STRERROR_R_CHAR_P
LWMsgStatus
lwmsg_strerror(
int err,
char** message
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
char* my_message = NULL;
char buffer[512];
const char* text = NULL;
text = strerror_r(err, buffer, sizeof(buffer));
if (!text)
{
text = "UNKNOWN";
}
my_message = strdup(text);
if (!my_message)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_MEMORY);
}
*message = my_message;
done:
return status;
error:
if (my_message)
{
free(my_message);
}
goto done;
}
#else
LWMsgStatus
lwmsg_strerror(
int err,
char** message
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
char* my_message = NULL;
char* temp = NULL;
size_t capacity = 128;
my_message = malloc(capacity);
if (!my_message)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_MEMORY);
}
while (strerror_r(err, my_message, capacity) == -1 &&
errno == ERANGE)
{
capacity *= 2;
temp = realloc(my_message, capacity);
if (!temp)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_MEMORY);
}
my_message = temp;
}
my_message[capacity-1] = '\0';
*message = my_message;
done:
return status;
error:
if (my_message)
{
free(my_message);
}
goto done;
}
#endif
#else
LWMsgStatus
lwmsg_strerror(
int err,
char** message
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
char* err_message = NULL;
char* my_message = NULL;
err_message = strerror(err);
my_message = strdup(err_message);
if (!my_message)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_MEMORY);
}
*message = my_message;
done:
return status;
error:
if (my_message)
{
free(my_message);
}
goto done;
}
#endif
LWMsgStatus
lwmsg_set_close_on_exec(
int fd
)
{
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
{
return lwmsg_error_map_errno(errno);
}
else
{
return LWMSG_STATUS_SUCCESS;
}
}
#if defined(__hpux__) || defined(__APPLE__) || defined(sun)
ssize_t
lwmsg_recvmsg_timeout(
int sock,
struct msghdr* msg,
int flags,
LWMsgTime* time
)
{
struct timeval timeout;
fd_set fds;
int ret = 0;
#ifdef MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
#endif
if (time && lwmsg_time_is_positive(time))
{
timeout.tv_sec = time->seconds;
timeout.tv_usec = time->microseconds;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = select(sock + 1, &fds, NULL, NULL, &timeout);
if (ret < 0)
{
return ret;
}
else if (ret == 0)
{
errno = EAGAIN;
return -1;
}
}
return recvmsg(sock, msg, flags);
}
ssize_t
lwmsg_sendmsg_timeout(
int sock,
const struct msghdr* msg,
int flags,
LWMsgTime* time
)
{
struct timeval timeout;
fd_set fds;
int ret = 0;
#ifdef MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
#endif
if (time && lwmsg_time_is_positive(time))
{
timeout.tv_sec = time->seconds;
timeout.tv_usec = time->microseconds;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = select(sock + 1, NULL, &fds, NULL, &timeout);
if (ret < 0)
{
return ret;
}
else if (ret == 0)
{
errno = EAGAIN;
return -1;
}
}
return sendmsg(sock, msg, flags);
}
#else
ssize_t
lwmsg_recvmsg_timeout(
int sock,
struct msghdr* msg,
int flags,
LWMsgTime* time
)
{
struct timeval timeout;
#ifdef MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
#endif
if (time && lwmsg_time_is_positive(time))
{
timeout.tv_sec = time->seconds;
timeout.tv_usec = time->microseconds;
}
else
{
memset(&timeout, 0, sizeof(timeout));
}
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)))
{
return -1;
}
return recvmsg(sock, msg, flags);
}
ssize_t
lwmsg_sendmsg_timeout(
int sock,
const struct msghdr* msg,
int flags,
LWMsgTime* time
)
{
struct timeval timeout;
#ifdef MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
#endif
if (time && lwmsg_time_is_positive(time))
{
timeout.tv_sec = time->seconds;
timeout.tv_usec = time->microseconds;
}
else
{
memset(&timeout, 0, sizeof(timeout));
}
if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)))
{
return -1;
}
return sendmsg(sock, msg, flags);
}
#endif