blob: 2a78cd637348e1e6f0f590e27e4565ae1acad993 [file] [edit]
/*
* Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <stdio.h>
#include <string.h>
#include "internal/cryptlib.h"
#include "crypto/ctype.h"
#include "internal/numbers.h"
#include <openssl/bio.h>
#include <openssl/configuration.h>
int BIO_printf(BIO *bio, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = BIO_vprintf(bio, format, args);
va_end(args);
return ret;
}
#if defined(_WIN32)
/*
* _MSC_VER described here:
* https://learn.microsoft.com/en-us/cpp/overview/compiler-versions?view=msvc-170
*
* Beginning with the UCRT in Visual Studio 2015 and Windows 10, snprintf is no
* longer identical to _snprintf. The snprintf behavior is now C99 standard
* conformant. The difference is that if you run out of buffer, snprintf
* null-terminates the end of the buffer and returns the number of characters
* that would have been required whereas _snprintf doesn't null-terminate the
* buffer and returns -1. Also, snprintf() includes one more character in the
* output because it doesn't null-terminate the buffer.
* [ https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/snprintf-snprintf-snprintf-l-snwprintf-snwprintf-l?view=msvc-170#remarks
*
* for older MSVC (older than 2015) we can use _vscprintf() and _vsnprintf()
* as suggested here:
* https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
*
*/
static int msvc_bio_vprintf(BIO *bio, const char *format, va_list args)
{
char buf[512];
char *abuf;
int ret, sz;
sz = _vsnprintf_s(buf, sizeof(buf), _TRUNCATE, format, args);
if (sz == -1) {
sz = _vscprintf(format, args) + 1;
abuf = (char *)OPENSSL_malloc(sz);
if (abuf == NULL) {
ret = -1;
} else {
sz = _vsnprintf(abuf, sz, format, args);
ret = BIO_write(bio, abuf, sz);
OPENSSL_free(abuf);
}
} else {
ret = BIO_write(bio, buf, sz);
}
return ret;
}
/*
* This function is for unit test on windows only when built with Visual Studio
*/
int ossl_BIO_snprintf_msvc(char *buf, size_t n, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = _vsnprintf_s(buf, n, _TRUNCATE, format, args);
va_end(args);
return ret;
}
#endif
int BIO_vprintf(BIO *bio, const char *format, va_list args)
{
va_list cp_args;
#if !defined(_MSC_VER) || _MSC_VER > 1900
int sz;
#endif
int ret = -1;
va_copy(cp_args, args);
#if defined(_MSC_VER) && _MSC_VER < 1900
ret = msvc_bio_vprintf(bio, format, cp_args);
#else
char buf[512];
char *abuf;
/*
* some compilers modify va_list, hence each call to v*printf()
* should operate with its own instance of va_list. The first
* call to vsnprintf() here uses args we got in function argument.
* The second call is going to use cp_args we made earlier.
*/
sz = vsnprintf(buf, sizeof(buf), format, args);
if (sz >= 0) {
if ((size_t)sz > sizeof(buf)) {
sz += 1;
abuf = (char *)OPENSSL_malloc(sz);
if (abuf == NULL) {
ret = -1;
} else {
sz = vsnprintf(abuf, sz, format, cp_args);
ret = BIO_write(bio, abuf, sz);
OPENSSL_free(abuf);
}
} else {
/* vsnprintf returns length not including nul-terminator */
ret = BIO_write(bio, buf, sz);
}
}
#endif
va_end(cp_args);
return ret;
}
/*
* For historical reasons BIO_snprintf and friends return a failure for string
* truncation (-1) instead of the POSIX requirement of a success with the
* number of characters that would have been written. Upon seeing -1 on
* return, the caller must treat output buf as unsafe (as a buf with missing
* nul terminator).
*/
int BIO_snprintf(char *buf, size_t n, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
#if defined(_MSC_VER) && _MSC_VER < 1900
ret = _vsnprintf_s(buf, n, _TRUNCATE, format, args);
#else
ret = vsnprintf(buf, n, format, args);
if ((size_t)ret >= n)
ret = -1;
#endif
va_end(args);
return ret;
}
int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args)
{
int ret;
#if defined(_MSC_VER) && _MSC_VER < 1900
ret = _vsnprintf_s(buf, n, _TRUNCATE, format, args);
#else
ret = vsnprintf(buf, n, format, args);
if ((size_t)ret >= n)
ret = -1;
#endif
return ret;
}