Initial push with a few changes

This commit is contained in:
Stig-Ørjan Smelror 2024-05-31 15:27:02 +02:00
parent 4716f1e016
commit 1fb029cde7
4 changed files with 890 additions and 1431 deletions

480
asn.c
View File

@ -30,314 +30,286 @@
static asn1_node *asn_defs = NULL; static asn1_node *asn_defs = NULL;
#define MAX_ERROR_DESCRIPTION_SIZE 1024 #define MAX_ERROR_DESCRIPTION_SIZE 1024
char errstr[MAX_ERROR_DESCRIPTION_SIZE]; static char errstr[MAX_ERROR_DESCRIPTION_SIZE];
/* Parse an ASN.1 BER header */ /* Parse an ASN.1 BER header */
RD_BOOL static int ber_parse_header(STREAM s, int tagval, uint32 *length)
ber_parse_header(STREAM s, int tagval, uint32 *length)
{ {
int tag, len; int tag, len;
if (tagval > 0xff) if (tagval > 0xff) {
{ if (!s_check_rem(s, 2)) {
if (!s_check_rem(s, 2)) { return ASN_PARSE_ERROR;
return False; }
} in_uint16_be(s, tag);
in_uint16_be(s, tag); } else {
} if (!s_check_rem(s, 1)) {
else return ASN_PARSE_ERROR;
{ }
if (!s_check_rem(s, 1)) { in_uint8(s, tag);
return False; }
}
in_uint8(s, tag);
}
if (tag != tagval) if (tag != tagval) {
{ logger(Core, Error, "ber_parse_header(), expected tag %d, got %d", tagval, tag);
logger(Core, Error, "ber_parse_header(), expected tag %d, got %d", tagval, tag); return ASN_TAG_ERROR;
return False; }
}
if (!s_check_rem(s, 1)) { if (!s_check_rem(s, 1)) {
return False; return ASN_PARSE_ERROR;
} }
in_uint8(s, len); in_uint8(s, len);
if (len & 0x80) if (len & 0x80) {
{ len &= ~0x80;
len &= ~0x80; if (!s_check_rem(s, len)) {
if (!s_check_rem(s, len)) { return ASN_PARSE_ERROR;
return False; }
} *length = 0;
*length = 0; while (len--) {
while (len--) next_be(s, *length);
next_be(s, *length); }
} } else {
else *length = len;
*length = len; }
return True; return ASN_SUCCESS;
} }
void static void ber_out_sequence(STREAM out, STREAM content)
ber_out_sequence(STREAM out, STREAM content)
{ {
size_t length; size_t length;
length = (content ? s_length(content) : 0); length = (content ? s_length(content) : 0);
ber_out_header(out, BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, length); ber_out_header(out, BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED, length);
if (content) if (content) {
out_stream(out, content); out_stream(out, content);
}
} }
/* Output an ASN.1 BER header */ /* Output an ASN.1 BER header */
void static void ber_out_header(STREAM s, int tagval, int length)
ber_out_header(STREAM s, int tagval, int length)
{ {
if (tagval > 0xff) if (tagval > 0xff) {
{ out_uint16_be(s, tagval);
out_uint16_be(s, tagval); } else {
} out_uint8(s, tagval);
else }
{
out_uint8(s, tagval);
}
if (length >= 0x80) if (length >= 0x80) {
{ out_uint8(s, 0x82);
out_uint8(s, 0x82); out_uint16_be(s, length);
out_uint16_be(s, length); } else {
} out_uint8(s, length);
else }
out_uint8(s, length);
} }
/* Output an ASN.1 BER integer */ /* Output an ASN.1 BER integer */
void static void ber_out_integer(STREAM s, int value)
ber_out_integer(STREAM s, int value)
{ {
ber_out_header(s, BER_TAG_INTEGER, 2); ber_out_header(s, BER_TAG_INTEGER, 2);
out_uint16_be(s, value); out_uint16_be(s, value);
} }
RD_BOOL static int ber_in_header(STREAM s, int *tagval, int *decoded_len)
ber_in_header(STREAM s, int *tagval, int *decoded_len)
{ {
in_uint8(s, *tagval); in_uint8(s, *tagval);
in_uint8(s, *decoded_len); in_uint8(s, *decoded_len);
if (*decoded_len < 0x80) if (*decoded_len < 0x80) {
return True; return ASN_SUCCESS;
else if (*decoded_len == 0x81) } else if (*decoded_len == 0x81) {
{ in_uint8(s, *decoded_len);
in_uint8(s, *decoded_len); return ASN_SUCCESS;
return True; } else if (*decoded_len == 0x82) {
} in_uint16_be(s, *decoded_len);
else if (*decoded_len == 0x82) return ASN_SUCCESS;
{ }
in_uint16_be(s, *decoded_len);
return True;
}
return False; return ASN_PARSE_ERROR;
} }
static int init_asn1_lib(void)
int init_asn1_lib(void)
{ {
int asn1_rv; int asn1_rv;
if (asn_defs) { if (asn_defs) {
return 0; return ASN_SUCCESS;
} }
asn_defs = malloc(sizeof(*asn_defs)); asn_defs = malloc(sizeof(*asn_defs));
if (!asn_defs) { if (!asn_defs) {
logger(Core, Error, "%s:%s:%d Failed to allocate memory for ASN.1 parser\n", logger(Core, Error, "%s:%s:%d Failed to allocate memory for ASN.1 parser\n",
__FILE__, __func__, __LINE__); __FILE__, __func__, __LINE__);
return 1; return ASN_MEM_ERROR;
} }
*asn_defs = NULL; *asn_defs = NULL;
if (ASN1_SUCCESS != (asn1_rv = asn1_array2tree(pkix_asn1_tab, asn_defs, errstr))) { if (ASN1_SUCCESS != (asn1_rv = asn1_array2tree(pkix_asn1_tab, asn_defs, errstr))) {
logger(Core, Error, "%s:%s:%d Failed to init ASN.1 parser. Error = 0x%x (%s)\n", logger(Core, Error, "%s:%s:%d Failed to init ASN.1 parser. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv)); __FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1; free(asn_defs);
} asn_defs = NULL;
return asn1_rv;
}
return 0; return ASN_SUCCESS;
} }
/* Encode RSA public key into DER PKCS#1 */ /* Encode RSA public key into DER PKCS#1 */
/* Returns; 0 - success, 1 - fatal error, 2 - insufficient space in buffer */ static int write_pkcs1_der_pubkey(const gnutls_datum_t *m, const gnutls_datum_t *e, uint8_t *out, int *out_len)
int write_pkcs1_der_pubkey(const gnutls_datum_t *m, const gnutls_datum_t *e, uint8_t *out, int *out_len)
{ {
int asn1_rv; int asn1_rv;
asn1_node asn_cert; asn1_node asn_cert;
if (!asn_defs) { if (!asn_defs) {
if (init_asn1_lib() != 0) { if (init_asn1_lib() != ASN_SUCCESS) {
return 1; return ASN_INIT_ERROR;
} }
} }
if (ASN1_SUCCESS != (asn1_rv = asn1_create_element(*asn_defs, "PKIX1Implicit88.RSAPublicKey", &asn_cert))) { if (ASN1_SUCCESS != (asn1_rv = asn1_create_element(*asn_defs, "PKIX1Implicit88.RSAPublicKey", &asn_cert))) {
logger(Core, Error, "%s:%s:%d Failed to create ASN.1 parser element for RSAPublicKey. Error = 0x%x (%s)\n", logger(Core, Error, "%s:%s:%d Failed to create ASN.1 parser element for RSAPublicKey. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv)); __FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1; return ASN_CREATE_ERROR;
} }
if (ASN1_SUCCESS != (asn1_rv = asn1_write_value(asn_cert, "modulus", m->data, m->size))) { if (ASN1_SUCCESS != (asn1_rv = asn1_write_value(asn_cert, "modulus", m->data, m->size))) {
logger(Core, Error, "%s:%s:%d Failed to write modulus. Error = 0x%x (%s)\n", logger(Core, Error, "%s:%s:%d Failed to write modulus. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv)); __FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1; return ASN_WRITE_ERROR;
} }
if (ASN1_SUCCESS != (asn1_rv = asn1_write_value(asn_cert, "publicExponent", e->data, e->size))) { if (ASN1_SUCCESS != (asn1_rv = asn1_write_value(asn_cert, "publicExponent", e->data, e->size))) {
logger(Core, Error, "%s:%s:%d Failed to write publicExponent. Error = 0x%x (%s)\n", logger(Core, Error, "%s:%s:%d Failed to write publicExponent. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv)); __FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1; return ASN_WRITE_ERROR;
} }
if (ASN1_SUCCESS != (asn1_rv = asn1_der_coding(asn_cert, "", out, out_len, errstr))) { if (ASN1_SUCCESS != (asn1_rv = asn1_der_coding(asn_cert, "", out, out_len, errstr))) {
logger(Core, Error, "%s:%s:%d Failed to encode PKIX1Implicit88.RSAPublicKey. Error = 0x%x (%s)\n", logger(Core, Error, "%s:%s:%d Failed to encode PKIX1Implicit88.RSAPublicKey. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv)); __FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
if (asn1_rv == ASN1_MEM_ERROR) { return ASN_ENCODE_ERROR;
return 2; }
}
return 1; return ASN_SUCCESS;
} }
return 0; static int libtasn_read_cert_pk(uint8_t *data, size_t len, char *oid, size_t *oid_size, gnutls_datum_t *m, gnutls_datum_t *e)
{
int asn1_rv;
asn1_node asn_cert;
/* Parse DER encoded x.509 certificate */
if (!asn_defs) {
if (init_asn1_lib() != ASN_SUCCESS) {
return ASN_INIT_ERROR;
}
}
if (ASN1_SUCCESS != (asn1_rv = asn1_create_element(*asn_defs, "PKIX1Implicit88.Certificate", &asn_cert))) {
logger(Core, Error, "%s:%s:%d Failed to create ASN.1 parser element. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return ASN_CREATE_ERROR;
}
if (ASN1_SUCCESS != (asn1_rv = asn1_der_decoding(&asn_cert, data, len, errstr))) {
logger(Core, Error, "%s:%s:%d Failed to decode certificate object. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return ASN_DECODE_ERROR;
}
if (oid && oid_size) {
if (ASN1_SUCCESS != (asn1_rv = asn1_read_value(asn_cert, "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm",
oid, (int *)oid_size)))
{
logger(Core, Error, "%s:%s:%d Failed to get cert's public key algorithm. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return ASN_READ_ERROR;
}
}
if (m && e) {
int buflen;
uint8_t buf[16384];
asn1_node asn_key;
int nblen;
uint8_t newbuf[16384];
buflen = sizeof(buf) - 1;
if (ASN1_SUCCESS != (asn1_rv = asn1_read_value(asn_cert, "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey", buf, &buflen))) {
logger(Core, Error, "%s:%s:%d Failed to get cert's public key. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return ASN_READ_ERROR;
}
if (ASN1_SUCCESS != (asn1_rv = asn1_create_element(*asn_defs, "PKIX1Implicit88.RSAPublicKey", &asn_key))) {
logger(Core, Error, "%s:%s:%d Failed to create ASN.1 parser element. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return ASN_CREATE_ERROR;
}
// As it' a BIT STRING the len constitutes the number of BITS, not BYTES
if (ASN1_SUCCESS != (asn1_rv = asn1_der_decoding(&asn_key, buf, buflen / 8, errstr))) {
logger(Core, Error, "%s:%s:%d Failed to decode public key object. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return ASN_DECODE_ERROR;
}
/* Get RSA public key's modulus and exponent */
nblen = sizeof(newbuf);
if (ASN1_SUCCESS != (asn1_rv = asn1_read_value(asn_key, "modulus", newbuf, &nblen))) {
logger(Core, Error, "%s:%s:%d Failed to get RSA public key's modulus. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return ASN_READ_ERROR;
}
m->size = nblen;
m->data = malloc(m->size);
if (!m->data) {
logger(Core, Error, "%s:%s:%d Failed to allocate memory for modulus.\n", __FILE__, __func__, __LINE__);
return ASN_MEM_ERROR;
}
memcpy((void *)m->data, newbuf, m->size);
nblen = sizeof(newbuf);
if (ASN1_SUCCESS != (asn1_rv = asn1_read_value(asn_key, "publicExponent", newbuf, &nblen))) {
logger(Core, Error, "%s:%s:%d Failed to get RSA public key's exponent. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
free(m->data);
return ASN_READ_ERROR;
}
e->size = nblen;
e->data = malloc(e->size);
if (!e->data) {
logger(Core, Error, "%s:%s:%d Failed to allocate memory for exponent.\n", __FILE__, __func__, __LINE__);
free(m->data);
return ASN_MEM_ERROR;
}
memcpy((void *)e->data, newbuf, e->size);
}
return ASN_SUCCESS;
} }
int libtasn_read_cert_pk_oid(uint8_t *data, size_t len, char *oid, size_t *oid_size) int libtasn_read_cert_pk_oid(uint8_t *data, size_t len, char *oid, size_t *oid_size)
{ {
int asn1_rv; return libtasn_read_cert_pk(data, len, oid, oid_size, NULL, NULL);
asn1_node asn_cert;
/* Parse DER encoded x.509 certificate */
if (!asn_defs) {
if (init_asn1_lib() != 0) {
return 1;
}
}
if (ASN1_SUCCESS != (asn1_rv = asn1_create_element(*asn_defs, "PKIX1Implicit88.Certificate", &asn_cert))) {
logger(Core, Error, "%s:%s:%d Failed to create ASN.1 parser element. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1;
}
if (ASN1_SUCCESS != (asn1_rv = asn1_der_decoding(&asn_cert, data, len, errstr))) {
logger(Core, Error, "%s:%s:%d Failed to decode certificate object. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1;
}
if (ASN1_SUCCESS != (asn1_rv = asn1_read_value(asn_cert, "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm",
oid, (int *)oid_size)))
{
logger(Core, Error, "%s:%s:%d Failed to get cert's public key algorithm. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1;
}
return 0;
} }
int libtasn_read_cert_pk_parameters(uint8_t *data, size_t len, gnutls_datum_t *m, gnutls_datum_t *e) int libtasn_read_cert_pk_parameters(uint8_t *data, size_t len, gnutls_datum_t *m, gnutls_datum_t *e)
{ {
int asn1_rv; return libtasn_read_cert_pk(data, len, NULL, NULL, m, e);
asn1_node asn_cert;
int buflen;
uint8_t buf[16384];
asn1_node asn_key;
int nblen;
uint8_t newbuf[16384];
/* Parse DER encoded x.509 certificate */
init_asn1_lib();
if (ASN1_SUCCESS != (asn1_rv = asn1_create_element(*asn_defs, "PKIX1Implicit88.Certificate", &asn_cert))) {
logger(Core, Error, "%s:%s:%d Failed to create ASN.1 parser element. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1;
}
if (ASN1_SUCCESS != (asn1_rv = asn1_der_decoding(&asn_cert, data, len, errstr))) {
logger(Core, Error, "%s:%s:%d Failed to decode certificate object. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1;
}
buflen = sizeof(buf) - 1;
if (ASN1_SUCCESS != (asn1_rv = asn1_read_value(asn_cert, "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey", buf, &buflen))) {
logger(Core, Error, "%s:%s:%d Failed to get cert's public key. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1;
}
if (ASN1_SUCCESS != (asn1_rv = asn1_create_element(*asn_defs, "PKIX1Implicit88.RSAPublicKey", &asn_key))) {
logger(Core, Error, "%s:%s:%d Failed to create ASN.1 parser element. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1;
}
// As it' a BIT STRING the len constitutes the number of BITS, not BYTES
if (ASN1_SUCCESS != (asn1_rv = asn1_der_decoding(&asn_key, buf, buflen / 8, errstr))) {
logger(Core, Error, "%s:%s:%d Failed to decode public key object. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1;
}
/* Get RSA public key's modulus and exponent */
nblen = sizeof(newbuf);
if (ASN1_SUCCESS != (asn1_rv = asn1_read_value(asn_key, "modulus", newbuf, &nblen))) {
logger(Core, Error, "%s:%s:%d Failed to get RSA public key's modulus. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1;
}
m->size = nblen;
if (!(m->data = malloc(m->size))) {
logger(Core, Error, "%s:%s:%d Failed to allocate memory for modulus.\n", __FILE__, __func__, __LINE__);
return 1;
}
memcpy((void *)m->data, newbuf, m->size);
nblen = sizeof(newbuf);
if (ASN1_SUCCESS != (asn1_rv = asn1_read_value(asn_key, "publicExponent", newbuf, &nblen))) {
logger(Core, Error, "%s:%s:%d Failed to get RSA public key's exponent. Error = 0x%x (%s)\n",
__FILE__, __func__, __LINE__, asn1_rv, asn1_strerror(asn1_rv));
return 1;
}
e->size = nblen;
if (!(e->data = malloc(e->size))) {
logger(Core, Error, "%s:%s:%d Failed to allocate memory for exponent.\n", __FILE__, __func__, __LINE__);
if (m->data) {
free(m->data);
}
return 1;
}
memcpy((void *)e->data, newbuf, e->size);
return 0;
} }

69
asn.h
View File

@ -16,8 +16,8 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef _RDASN_H #ifndef RDASN_H
#define _RDASN_H #define RDASN_H
#include <gnutls/gnutls.h> #include <gnutls/gnutls.h>
#include <libtasn1.h> #include <libtasn1.h>
@ -29,17 +29,68 @@
extern "C" { extern "C" {
#endif #endif
#define OID_SHA_WITH_RSA_SIGNATURE "1.3.14.3.2.15" #define OID_SHA_WITH_RSA_SIGNATURE "1.3.14.3.2.15"
#define OID_MD5_WITH_RSA_SIGNATURE "1.3.14.3.2.25" #define OID_MD5_WITH_RSA_SIGNATURE "1.3.14.3.2.25"
int init_asn1_lib(void); /**
int write_pkcs1_der_pubkey(const gnutls_datum_t *m, const gnutls_datum_t *e, uint8_t *out, int *out_len); * Initialize the ASN.1 library.
*
* This function initializes the ASN.1 library and should be called before any other ASN.1 functions.
*
* Returns: 0 on success, or a non-zero error code on failure.
*/
extern int init_asn1_lib(void);
int libtasn_read_cert_pk_oid(uint8_t *data, size_t len, char *oid, size_t *oid_size); /**
int libtasn_read_cert_pk_parameters(uint8_t *data, size_t len, gnutls_datum_t *m, gnutls_datum_t *e); * Encode an RSA public key into DER PKCS#1 format.
*
* This function encodes an RSA public key into DER PKCS#1 format. The modulus and exponent of the public key are
* provided as input, and the encoded key is returned in the output buffer.
*
* Parameters:
* - m: A pointer to a gnutls_datum_t structure that contains the modulus of the public key.
* - e: A pointer to a gnutls_datum_t structure that contains the exponent of the public key.
* - out: A pointer to the output buffer where the encoded key will be stored.
* - out_len: A pointer to an integer that will be set to the length of the encoded key.
*
* Returns: 0 on success, or a non-zero error code on failure.
*/
extern int write_pkcs1_der_pubkey(const gnutls_datum_t *m, const gnutls_datum_t *e, uint8_t *out, int *out_len);
/**
* Read the public key algorithm OID from a DER encoded x.509 certificate.
*
* This function reads the public key algorithm OID from a DER encoded x.509 certificate. The certificate is provided
* as input, and the OID is returned in the output buffer.
*
* Parameters:
* - data: A pointer to the input buffer that contains the DER encoded x.509 certificate.
* - len: The length of the input buffer.
* - oid: A pointer to the output buffer where the OID will be stored.
* - oid_size: A pointer to an integer that will be set to the length of the OID.
*
* Returns: 0 on success, or a non-zero error code on failure.
*/
extern int libtasn_read_cert_pk_oid(uint8_t *data, size_t len, char *oid, size_t *oid_size);
/**
* Read the public key parameters from a DER encoded x.509 certificate.
*
* This function reads the public key parameters (modulus and exponent) from a DER encoded x.509 certificate. The
* certificate is provided as input, and the parameters are returned in the output buffers.
*
* Parameters:
* - data: A pointer to the input buffer that contains the DER encoded x.509 certificate.
* - len: The length of the input buffer.
* - m: A pointer to a gnutls_datum_t structure that will be set to the modulus of the public key.
* - e: A pointer to a gnutls_datum_t structure that will be set to the exponent of the public key.
*
* Returns: 0 on success, or a non-zero error code on failure.
*/
extern int libtasn_read_cert_pk_parameters(uint8_t *data, size_t len, gnutls_datum_t *m, gnutls_datum_t *e);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* _RDASN_H */ #endif /* RDASN_H */

1195
bitmap.c

File diff suppressed because it is too large Load Diff

577
cache.c
View File

@ -20,6 +20,9 @@
*/ */
#include "rdesktop.h" #include "rdesktop.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
/* BITMAP CACHE */ /* BITMAP CACHE */
extern int g_pstcache_fd[]; extern int g_pstcache_fd[];
@ -30,22 +33,15 @@ extern int g_pstcache_fd[];
#define NOT_SET -1 #define NOT_SET -1
#define IS_SET(idx) (idx >= 0) #define IS_SET(idx) (idx >= 0)
/*
* TODO: Test for optimal value of BUMP_COUNT. TO_TOP gives lowest CPU utilisation but using
* a positive value will hopefully result in less frequently used bitmaps having a greater chance
* of being evicted from the cache, and thereby reducing the need to load bitmaps from disk.
* (Jeroen)
*/
#define BUMP_COUNT 40 #define BUMP_COUNT 40
struct bmpcache_entry typedef struct bmpcache_entry {
{ RD_HBITMAP bitmap;
RD_HBITMAP bitmap; int16_t previous;
sint16 previous; int16_t next;
sint16 next; } bmpcache_entry_t;
};
static struct bmpcache_entry g_bmpcache[3][0xa00]; static bmpcache_entry_t g_bmpcache[3][0xa00];
static RD_HBITMAP g_volatile_bc[3]; static RD_HBITMAP g_volatile_bc[3];
static int g_bmpcache_lru[3] = { NOT_SET, NOT_SET, NOT_SET }; static int g_bmpcache_lru[3] = { NOT_SET, NOT_SET, NOT_SET };
@ -54,386 +50,310 @@ static int g_bmpcache_mru[3] = { NOT_SET, NOT_SET, NOT_SET };
static int g_bmpcache_count[3]; static int g_bmpcache_count[3];
/* Setup the bitmap cache lru/mru linked list */ /* Setup the bitmap cache lru/mru linked list */
void void cache_rebuild_bmpcache_linked_list(uint8_t id, int16_t *idx, int count) {
cache_rebuild_bmpcache_linked_list(uint8 id, sint16 * idx, int count) int n = count, c = 0;
{ int16_t n_idx;
int n = count, c = 0;
sint16 n_idx;
/* find top, skip evicted bitmaps */ /* find top, skip evicted bitmaps */
while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL); while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL);
if (n < 0) if (n < 0) {
{ g_bmpcache_mru[id] = g_bmpcache_lru[id] = NOT_SET;
g_bmpcache_mru[id] = g_bmpcache_lru[id] = NOT_SET; return;
return; }
}
g_bmpcache_mru[id] = idx[n]; g_bmpcache_mru[id] = idx[n];
g_bmpcache[id][idx[n]].next = NOT_SET; g_bmpcache[id][idx[n]].next = NOT_SET;
n_idx = idx[n]; n_idx = idx[n];
c++; c++;
/* link list */ /* link list */
while (n >= 0) while (n >= 0) {
{ /* skip evicted bitmaps */
/* skip evicted bitmaps */ while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL);
while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL);
if (n < 0) if (n < 0)
break; break;
g_bmpcache[id][n_idx].previous = idx[n]; g_bmpcache[id][n_idx].previous = idx[n];
g_bmpcache[id][idx[n]].next = n_idx; g_bmpcache[id][idx[n]].next = n_idx;
n_idx = idx[n]; n_idx = idx[n];
c++; c++;
} }
g_bmpcache[id][n_idx].previous = NOT_SET; g_bmpcache[id][n_idx].previous = NOT_SET;
g_bmpcache_lru[id] = n_idx; g_bmpcache_lru[id] = n_idx;
if (c != g_bmpcache_count[id]) if (c != g_bmpcache_count[id]) {
{ logger(Core, Error,
logger(Core, Error, "cache_rebuild_bmpcache_linked_list(), %d in bitmap cache linked list, %d in ui cache...",
"cache_rebuild_bmpcache_linked_list(), %d in bitmap cache linked list, %d in ui cache...", c, g_bmpcache_count[id]);
c, g_bmpcache_count[id]); exit(EXIT_FAILURE);
exit(EX_SOFTWARE); }
}
} }
/* Move a bitmap to a new position in the linked list. */ /* Move a bitmap to a new position in the linked list. */
void void cache_bump_bitmap(uint8_t id, uint16_t idx, int bump) {
cache_bump_bitmap(uint8 id, uint16 idx, int bump) int p_idx, n_idx, n;
{
int p_idx, n_idx, n;
if (!IS_PERSISTENT(id)) if (!IS_PERSISTENT(id))
return; return;
if (g_bmpcache_mru[id] == idx) if (g_bmpcache_mru[id] == idx)
return; return;
logger(Core, Debug, "cache_bump_bitmap(), id=%d, idx=%d, bump=%d", id, idx, bump); logger(Core, Debug, "cache_bump_bitmap(), id=%d, idx=%d, bump=%d", id, idx, bump);
n_idx = g_bmpcache[id][idx].next; n_idx = g_bmpcache[id][idx].next;
p_idx = g_bmpcache[id][idx].previous; p_idx = g_bmpcache[id][idx].previous;
if (IS_SET(n_idx)) if (IS_SET(n_idx)) {
{ /* remove */
/* remove */ --g_bmpcache_count[id];
--g_bmpcache_count[id]; if (IS_SET(p_idx))
if (IS_SET(p_idx)) g_bmpcache[id][p_idx].next = n_idx;
g_bmpcache[id][p_idx].next = n_idx; else
else g_bmpcache_lru[id] = n_idx;
g_bmpcache_lru[id] = n_idx; if (IS_SET(n_idx))
if (IS_SET(n_idx)) g_bmpcache[id][n_idx].previous = p_idx;
g_bmpcache[id][n_idx].previous = p_idx; else
else g_bmpcache_mru[id] = p_idx;
g_bmpcache_mru[id] = p_idx; } else {
} p_idx = NOT_SET;
else n_idx = g_bmpcache_lru[id];
{ }
p_idx = NOT_SET;
n_idx = g_bmpcache_lru[id];
}
if (bump >= 0) if (bump >= 0) {
{ for (n = 0; n < bump && IS_SET(n_idx); n++) {
for (n = 0; n < bump && IS_SET(n_idx); n++) p_idx = n_idx;
{ n_idx = g_bmpcache[id][p_idx].next;
p_idx = n_idx; }
n_idx = g_bmpcache[id][p_idx].next; } else {
} p_idx = g_bmpcache_mru[id];
} n_idx = NOT_SET;
else }
{
p_idx = g_bmpcache_mru[id];
n_idx = NOT_SET;
}
/* insert */ /* insert */
++g_bmpcache_count[id]; ++g_bmpcache_count[id];
g_bmpcache[id][idx].previous = p_idx; g_bmpcache[id][idx].previous = p_idx;
g_bmpcache[id][idx].next = n_idx; g_bmpcache[id][idx].next = n_idx;
if (p_idx >= 0) if (p_idx >= 0)
g_bmpcache[id][p_idx].next = idx; g_bmpcache[id][p_idx].next = idx;
else else
g_bmpcache_lru[id] = idx; g_bmpcache_lru[id] = idx;
if (n_idx >= 0) if (n_idx >= 0)
g_bmpcache[id][n_idx].previous = idx; g_bmpcache[id][n_idx].previous = idx;
else else
g_bmpcache_mru[id] = idx; g_bmpcache_mru[id] = idx;
} }
/* Evict the least-recently used bitmap from the cache */ /* Evict the least-recently used bitmap from the cache */
void void cache_evict_bitmap(uint8_t id) {
cache_evict_bitmap(uint8 id) uint16_t idx;
{ int n_idx;
uint16 idx;
int n_idx;
if (!IS_PERSISTENT(id)) if (!IS_PERSISTENT(id))
return; return;
idx = g_bmpcache_lru[id]; idx = g_bmpcache_lru[id];
n_idx = g_bmpcache[id][idx].next; n_idx = g_bmpcache[id][idx].next;
logger(Core, Debug, "cache_evict_bitmap(), id=%d idx=%d n_idx=%d bmp=%p", id, idx, n_idx, logger(Core, Debug, "cache_evict_bitmap(), id=%d idx=%d n_idx=%d bmp=%p", id, idx, n_idx,
g_bmpcache[id][idx].bitmap); g_bmpcache[id][idx].bitmap);
ui_destroy_bitmap(g_bmpcache[id][idx].bitmap); ui_destroy_bitmap(g_bmpcache[id][idx].bitmap);
--g_bmpcache_count[id]; --g_bmpcache_count[id];
g_bmpcache[id][idx].bitmap = 0; g_bmpcache[id][idx].bitmap = 0;
g_bmpcache_lru[id] = n_idx; g_bmpcache_lru[id] = n_idx;
g_bmpcache[id][n_idx].previous = NOT_SET; g_bmpcache[id][n_idx].previous = NOT_SET;
pstcache_touch_bitmap(id, idx, 0); pstcache_touch_bitmap(id, idx, 0);
} }
/* Retrieve a bitmap from the cache */ /* Retrieve a bitmap from the cache */
RD_HBITMAP RD_HBITMAP cache_get_bitmap(uint8_t id, uint16_t idx) {
cache_get_bitmap(uint8 id, uint16 idx) if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0]))) {
{ if (g_bmpcache[id][idx].bitmap || pstcache_load_bitmap(id, idx)) {
if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0]))) if (IS_PERSISTENT(id))
{ cache_bump_bitmap(id, idx, BUMP_COUNT);
if (g_bmpcache[id][idx].bitmap || pstcache_load_bitmap(id, idx))
{
if (IS_PERSISTENT(id))
cache_bump_bitmap(id, idx, BUMP_COUNT);
return g_bmpcache[id][idx].bitmap; return g_bmpcache[id][idx].bitmap;
} }
} } else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff)) {
else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff)) return g_volatile_bc[id];
{ }
return g_volatile_bc[id];
}
logger(Core, Debug, "cache_get_bitmap(), id=%d, idx=%d", id, idx); logger(Core, Debug, "cache_get_bitmap(), id=%d, idx=%d", id, idx);
return NULL; return NULL;
} }
/* Store a bitmap in the cache */ /* Store a bitmap in the cache */
void void cache_put_bitmap(uint8_t id, uint16_t idx, RD_HBITMAP bitmap) {
cache_put_bitmap(uint8 id, uint16 idx, RD_HBITMAP bitmap) RD_HBITMAP old;
{
RD_HBITMAP old;
if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0]))) if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0]))) {
{ old = g_bmpcache[id][idx].bitmap;
old = g_bmpcache[id][idx].bitmap; if (old != NULL)
if (old != NULL) ui_destroy_bitmap(old);
ui_destroy_bitmap(old); g_bmpcache[id][idx].bitmap = bitmap;
g_bmpcache[id][idx].bitmap = bitmap;
if (IS_PERSISTENT(id)) if (IS_PERSISTENT(id)) {
{ if (old == NULL)
if (old == NULL) g_bmpcache[id][idx].previous = g_bmpcache[id][idx].next = NOT_SET;
g_bmpcache[id][idx].previous = g_bmpcache[id][idx].next = NOT_SET;
cache_bump_bitmap(id, idx, TO_TOP); cache_bump_bitmap(id, idx, TO_TOP);
if (g_bmpcache_count[id] > BMPCACHE2_C2_CELLS) if (g_bmpcache_count[id] > BMPCACHE2_C2_CELLS)
cache_evict_bitmap(id); cache_evict_bitmap(id);
} }
} } else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff)) {
else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff)) old = g_volatile_bc[id];
{ if (old != NULL)
old = g_volatile_bc[id]; ui_destroy_bitmap(old);
if (old != NULL) g_volatile_bc[id] = bitmap;
ui_destroy_bitmap(old); } else {
g_volatile_bc[id] = bitmap; logger(Core, Error, "cache_put_bitmap(), failed, id=%d, idx=%d\n", id, idx);
} }
else
{
logger(Core, Error, "cache_put_bitmap(), failed, id=%d, idx=%d\n", id, idx);
}
} }
/* Updates the persistent bitmap cache MRU information on exit */ /* Updates the persistent bitmap cache MRU information on exit */
void void cache_save_state(void) {
cache_save_state(void) uint32_t id = 0, t = 0;
{ int idx;
uint32 id = 0, t = 0;
int idx;
for (id = 0; id < NUM_ELEMENTS(g_bmpcache); id++) for (id = 0; id < NUM_ELEMENTS(g_bmpcache); id++)
if (IS_PERSISTENT(id)) if (IS_PERSISTENT(id)) {
{ logger(Core, Debug,
logger(Core, Debug, "cache_save_state(), saving cache state for bitmap cache %d", id);
"cache_save_state(), saving cache state for bitmap cache %d", id); idx = g_bmpcache_lru[id];
idx = g_bmpcache_lru[id]; while (idx >= 0) {
while (idx >= 0) pstcache_touch_bitmap(id, idx, ++t);
{ idx = g_bmpcache[id][idx].next;
pstcache_touch_bitmap(id, idx, ++t); }
idx = g_bmpcache[id][idx].next; logger(Core, Debug, "cache_save_state(), %d stamps written", t);
} }
logger(Core, Debug, "cache_save_state(), %d stamps written", t);
}
} }
/* FONT CACHE */ /* FONT CACHE */
static FONTGLYPH g_fontcache[12][256]; static FONTGLYPH g_fontcache[12][256];
/* Retrieve a glyph from the font cache */ /* Retrieve a glyph from the font cache */
FONTGLYPH * FONTGLYPH *cache_get_font(uint8_t font, uint16_t character) {
cache_get_font(uint8 font, uint16 character) if ((font < NUM_ELEMENTS(g_fontcache)) && (character < NUM_ELEMENTS(g_fontcache[0]))) {
{ FONTGLYPH *glyph = &g_fontcache[font][character];
FONTGLYPH *glyph; if (glyph->pixmap != NULL)
return glyph;
}
if ((font < NUM_ELEMENTS(g_fontcache)) && (character < NUM_ELEMENTS(g_fontcache[0]))) logger(Core, Debug, "cache_get_font(), font=%d, char=%d", font, character);
{ return NULL;
glyph = &g_fontcache[font][character];
if (glyph->pixmap != NULL)
return glyph;
}
logger(Core, Debug, "cache_get_font(), font=%d, char=%d", font, character);
return NULL;
} }
/* Store a glyph in the font cache */ /* Store a glyph in the font cache */
void void cache_put_font(uint8_t font, uint16_t character, uint16_t offset,
cache_put_font(uint8 font, uint16 character, uint16 offset, uint16_t baseline, uint16_t width, uint16_t height, RD_HGLYPH pixmap) {
uint16 baseline, uint16 width, uint16 height, RD_HGLYPH pixmap) if ((font < NUM_ELEMENTS(g_fontcache)) && (character < NUM_ELEMENTS(g_fontcache[0]))) {
{ FONTGLYPH *glyph = &g_fontcache[font][character];
FONTGLYPH *glyph; if (glyph->pixmap != NULL)
ui_destroy_glyph(glyph->pixmap);
if ((font < NUM_ELEMENTS(g_fontcache)) && (character < NUM_ELEMENTS(g_fontcache[0]))) glyph->offset = offset;
{ glyph->baseline = baseline;
glyph = &g_fontcache[font][character]; glyph->width = width;
if (glyph->pixmap != NULL) glyph->height = height;
ui_destroy_glyph(glyph->pixmap); glyph->pixmap = pixmap;
} else {
glyph->offset = offset; logger(Core, Error, "cache_put_font(), failed, font=%d, char=%d", font, character);
glyph->baseline = baseline; }
glyph->width = width;
glyph->height = height;
glyph->pixmap = pixmap;
}
else
{
logger(Core, Error, "cache_put_font(), failed, font=%d, char=%d", font, character);
}
} }
/* TEXT CACHE */ /* TEXT CACHE */
static DATABLOB g_textcache[256]; static DATABLOB g_textcache[256];
/* Retrieve a text item from the cache */ /* Retrieve a text item from the cache */
DATABLOB * DATABLOB *cache_get_text(uint8_t cache_id) {
cache_get_text(uint8 cache_id) return &g_textcache[cache_id];
{
DATABLOB *text;
text = &g_textcache[cache_id];
return text;
} }
/* Store a text item in the cache */ /* Store a text item in the cache */
void void cache_put_text(uint8_t cache_id, void *data, int length) {
cache_put_text(uint8 cache_id, void *data, int length) if (cache_id < NUM_ELEMENTS(g_textcache)) {
{ DATABLOB *text = &g_textcache[cache_id];
DATABLOB *text; if (text->data != NULL)
xfree(text->data);
text = &g_textcache[cache_id]; text->data = xmalloc(length);
if (text->data != NULL) text->size = length;
xfree(text->data); memcpy(text->data, data, length);
text->data = xmalloc(length); } else {
text->size = length; logger(Core, Error, "cache_put_text(), failed, cache_id=%d", cache_id);
memcpy(text->data, data, length); }
} }
/* DESKTOP CACHE */ /* DESKTOP CACHE */
static uint8 g_deskcache[0x38400 * 4]; static uint8_t g_deskcache[0x38400 * 4];
/* Retrieve desktop data from the cache */ /* Retrieve desktop data from the cache */
uint8 * uint8_t *cache_get_desktop(uint32_t offset, int cx, int cy, int bytes_per_pixel) {
cache_get_desktop(uint32 offset, int cx, int cy, int bytes_per_pixel) int length = cx * cy * bytes_per_pixel;
{
int length = cx * cy * bytes_per_pixel;
if (offset > sizeof(g_deskcache)) if (offset > sizeof(g_deskcache))
offset = 0; offset = 0;
if ((offset + length) <= sizeof(g_deskcache)) if ((offset + length) <= sizeof(g_deskcache)) {
{ return &g_deskcache[offset];
return &g_deskcache[offset]; }
}
logger(Core, Debug, "cache_get_desktop(), offset=%d, length=%d", offset, length); logger(Core, Debug, "cache_get_desktop(), offset=%d, length=%d", offset, length);
return NULL; return NULL;
} }
/* Store desktop data in the cache */ /* Store desktop data in the cache */
void void cache_put_desktop(uint32_t offset, int cx, int cy, int scanline, int bytes_per_pixel, uint8_t *data) {
cache_put_desktop(uint32 offset, int cx, int cy, int scanline, int bytes_per_pixel, uint8 * data) int length = cx * cy * bytes_per_pixel;
{
int length = cx * cy * bytes_per_pixel;
if (offset > sizeof(g_deskcache)) if (offset > sizeof(g_deskcache))
offset = 0; offset = 0;
if ((offset + length) <= sizeof(g_deskcache)) if ((offset + length) <= sizeof(g_deskcache)) {
{ cx *= bytes_per_pixel;
cx *= bytes_per_pixel; while (cy--) {
while (cy--) memcpy(&g_deskcache[offset], data, cx);
{ data += scanline;
memcpy(&g_deskcache[offset], data, cx); offset += cx;
data += scanline; }
offset += cx; } else {
} logger(Core, Error, "cache_put_desktop(), offset=%d, length=%d", offset, length);
} }
else
{
logger(Core, Error, "cache_put_desktop(), offset=%d, length=%d", offset, length);
}
} }
/* CURSOR CACHE */ /* CURSOR CACHE */
static RD_HCURSOR g_cursorcache[0x20]; static RD_HCURSOR g_cursorcache[0x20];
/* Retrieve cursor from cache */ /* Retrieve cursor from cache */
RD_HCURSOR RD_HCURSOR cache_get_cursor(uint16_t cache_idx) {
cache_get_cursor(uint16 cache_idx) if (cache_idx < NUM_ELEMENTS(g_cursorcache)) {
{ RD_HCURSOR cursor = g_cursorcache[cache_idx];
RD_HCURSOR cursor; if (cursor != NULL)
return cursor;
}
if (cache_idx < NUM_ELEMENTS(g_cursorcache)) logger(Core, Debug, "cache_get_cursor(), idx=%d", cache_idx);
{ return NULL;
cursor = g_cursorcache[cache_idx];
if (cursor != NULL)
return cursor;
}
logger(Core, Debug, "cache_get_cursor(), idx=%d", cache_idx);
return NULL;
} }
/* Store cursor in cache */ /* Store cursor in cache */
void void cache_put_cursor(uint16_t cache_idx, RD_HCURSOR cursor) {
cache_put_cursor(uint16 cache_idx, RD_HCURSOR cursor) if (cache_idx < NUM_ELEMENTS(g_cursorcache)) {
{ RD_HCURSOR old = g_cursorcache[cache_idx];
RD_HCURSOR old; if (old != NULL)
ui_destroy_cursor(old);
if (cache_idx < NUM_ELEMENTS(g_cursorcache)) g_cursorcache[cache_idx] = cursor;
{ } else {
old = g_cursorcache[cache_idx]; logger(Core, Error, "cache_put_cursor(), failed, idx=%d", cache_idx);
if (old != NULL) }
ui_destroy_cursor(old);
g_cursorcache[cache_idx] = cursor;
}
else
{
logger(Core, Error, "cache_put_cursor(), failed, idx=%d", cache_idx);
}
} }
/* BRUSH CACHE */ /* BRUSH CACHE */
@ -441,37 +361,26 @@ cache_put_cursor(uint16 cache_idx, RD_HCURSOR cursor)
static BRUSHDATA g_brushcache[2][64]; static BRUSHDATA g_brushcache[2][64];
/* Retrieve brush from cache */ /* Retrieve brush from cache */
BRUSHDATA * BRUSHDATA *cache_get_brush_data(uint8_t colour_code, uint8_t idx) {
cache_get_brush_data(uint8 colour_code, uint8 idx) colour_code = colour_code == 1 ? 0 : 1;
{ if (idx < NUM_ELEMENTS(g_brushcache[0])) {
colour_code = colour_code == 1 ? 0 : 1; return &g_brushcache[colour_code][idx];
if (idx < NUM_ELEMENTS(g_brushcache[0])) }
{ logger(Core, Debug, "cache_get_brush_data(), colour=%d, idx=%d", colour_code, idx);
return &g_brushcache[colour_code][idx]; return NULL;
}
logger(Core, Debug, "cache_get_brush_data(), colour=%d, idx=%d", colour_code, idx);
return NULL;
} }
/* Store brush in cache */ /* Store brush in cache */
/* this function takes over the data pointer in struct, e.g. caller gives it up */ /* this function takes over the data pointer in struct, e.g. caller gives it up */
void void cache_put_brush_data(uint8_t colour_code, uint8_t idx, BRUSHDATA *brush_data) {
cache_put_brush_data(uint8 colour_code, uint8 idx, BRUSHDATA * brush_data) colour_code = colour_code == 1 ? 0 : 1;
{ if (idx < NUM_ELEMENTS(g_brushcache[0])) {
BRUSHDATA *bd; BRUSHDATA *bd = &g_brushcache[colour_code][idx];
if (bd->data != 0) {
colour_code = colour_code == 1 ? 0 : 1; xfree(bd->data);
if (idx < NUM_ELEMENTS(g_brushcache[0])) }
{ memcpy(bd, brush_data, sizeof(BRUSHDATA));
bd = &g_brushcache[colour_code][idx]; } else {
if (bd->data != 0) logger(Core, Error, "cache_put_brush_data(), colour=%d, idx=%d", colour_code, idx);
{ }
xfree(bd->data);
}
memcpy(bd, brush_data, sizeof(BRUSHDATA));
}
else
{
logger(Core, Error, "cache_put_brush_data(), colour=%d, idx=%d", colour_code, idx);
}
} }