mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 09:58:01 +00:00
d36a4c3685
malloc functions, which automatically cast to the same type they're allocating the size of. Should prevent any future errors involving mallocing the size of the wrong structure type, and will also make life easier if we ever need to turn the PuTTY core code from real C into C++-friendly C. I haven't touched the Mac frontend in this checkin because I couldn't compile or test it. [originally from svn r3014]
430 lines
9.3 KiB
C
430 lines
9.3 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
#include "putty.h"
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* String handling routines.
|
|
*/
|
|
|
|
char *dupstr(const char *s)
|
|
{
|
|
int len = strlen(s);
|
|
char *p = snewn(len + 1, char);
|
|
strcpy(p, s);
|
|
return p;
|
|
}
|
|
|
|
/* Allocate the concatenation of N strings. Terminate arg list with NULL. */
|
|
char *dupcat(const char *s1, ...)
|
|
{
|
|
int len;
|
|
char *p, *q, *sn;
|
|
va_list ap;
|
|
|
|
len = strlen(s1);
|
|
va_start(ap, s1);
|
|
while (1) {
|
|
sn = va_arg(ap, char *);
|
|
if (!sn)
|
|
break;
|
|
len += strlen(sn);
|
|
}
|
|
va_end(ap);
|
|
|
|
p = snewn(len + 1, char);
|
|
strcpy(p, s1);
|
|
q = p + strlen(p);
|
|
|
|
va_start(ap, s1);
|
|
while (1) {
|
|
sn = va_arg(ap, char *);
|
|
if (!sn)
|
|
break;
|
|
strcpy(q, sn);
|
|
q += strlen(q);
|
|
}
|
|
va_end(ap);
|
|
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* Do an sprintf(), but into a custom-allocated buffer.
|
|
*
|
|
* Irritatingly, we don't seem to be able to do this portably using
|
|
* vsnprintf(), because there appear to be issues with re-using the
|
|
* same va_list for two calls, and the excellent C99 va_copy is not
|
|
* yet widespread. Bah. Instead I'm going to do a horrid, horrid
|
|
* hack, in which I trawl the format string myself, work out the
|
|
* maximum length of each format component, and resize the buffer
|
|
* before printing it.
|
|
*/
|
|
char *dupprintf(const char *fmt, ...)
|
|
{
|
|
char *ret;
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
ret = dupvprintf(fmt, ap);
|
|
va_end(ap);
|
|
return ret;
|
|
}
|
|
char *dupvprintf(const char *fmt, va_list ap)
|
|
{
|
|
char *buf;
|
|
int len, size;
|
|
|
|
buf = snewn(512, char);
|
|
size = 512;
|
|
|
|
while (1) {
|
|
#ifdef _WINDOWS
|
|
#define vsnprintf _vsnprintf
|
|
#endif
|
|
len = vsnprintf(buf, size, fmt, ap);
|
|
if (len >= 0 && len < size) {
|
|
/* This is the C99-specified criterion for snprintf to have
|
|
* been completely successful. */
|
|
return buf;
|
|
} else if (len > 0) {
|
|
/* This is the C99 error condition: the returned length is
|
|
* the required buffer size not counting the NUL. */
|
|
size = len + 1;
|
|
} else {
|
|
/* This is the pre-C99 glibc error condition: <0 means the
|
|
* buffer wasn't big enough, so we enlarge it a bit and hope. */
|
|
size += 512;
|
|
}
|
|
buf = sresize(buf, size, char);
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* Base64 encoding routine. This is required in public-key writing
|
|
* but also in HTTP proxy handling, so it's centralised here.
|
|
*/
|
|
|
|
void base64_encode_atom(unsigned char *data, int n, char *out)
|
|
{
|
|
static const char base64_chars[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
unsigned word;
|
|
|
|
word = data[0] << 16;
|
|
if (n > 1)
|
|
word |= data[1] << 8;
|
|
if (n > 2)
|
|
word |= data[2];
|
|
out[0] = base64_chars[(word >> 18) & 0x3F];
|
|
out[1] = base64_chars[(word >> 12) & 0x3F];
|
|
if (n > 1)
|
|
out[2] = base64_chars[(word >> 6) & 0x3F];
|
|
else
|
|
out[2] = '=';
|
|
if (n > 2)
|
|
out[3] = base64_chars[word & 0x3F];
|
|
else
|
|
out[3] = '=';
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* Generic routines to deal with send buffers: a linked list of
|
|
* smallish blocks, with the operations
|
|
*
|
|
* - add an arbitrary amount of data to the end of the list
|
|
* - remove the first N bytes from the list
|
|
* - return a (pointer,length) pair giving some initial data in
|
|
* the list, suitable for passing to a send or write system
|
|
* call
|
|
* - retrieve a larger amount of initial data from the list
|
|
* - return the current size of the buffer chain in bytes
|
|
*/
|
|
|
|
#define BUFFER_GRANULE 512
|
|
|
|
struct bufchain_granule {
|
|
struct bufchain_granule *next;
|
|
int buflen, bufpos;
|
|
char buf[BUFFER_GRANULE];
|
|
};
|
|
|
|
void bufchain_init(bufchain *ch)
|
|
{
|
|
ch->head = ch->tail = NULL;
|
|
ch->buffersize = 0;
|
|
}
|
|
|
|
void bufchain_clear(bufchain *ch)
|
|
{
|
|
struct bufchain_granule *b;
|
|
while (ch->head) {
|
|
b = ch->head;
|
|
ch->head = ch->head->next;
|
|
sfree(b);
|
|
}
|
|
ch->tail = NULL;
|
|
ch->buffersize = 0;
|
|
}
|
|
|
|
int bufchain_size(bufchain *ch)
|
|
{
|
|
return ch->buffersize;
|
|
}
|
|
|
|
void bufchain_add(bufchain *ch, const void *data, int len)
|
|
{
|
|
const char *buf = (const char *)data;
|
|
|
|
ch->buffersize += len;
|
|
|
|
if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) {
|
|
int copylen = min(len, BUFFER_GRANULE - ch->tail->buflen);
|
|
memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen);
|
|
buf += copylen;
|
|
len -= copylen;
|
|
ch->tail->buflen += copylen;
|
|
}
|
|
while (len > 0) {
|
|
int grainlen = min(len, BUFFER_GRANULE);
|
|
struct bufchain_granule *newbuf;
|
|
newbuf = snew(struct bufchain_granule);
|
|
newbuf->bufpos = 0;
|
|
newbuf->buflen = grainlen;
|
|
memcpy(newbuf->buf, buf, grainlen);
|
|
buf += grainlen;
|
|
len -= grainlen;
|
|
if (ch->tail)
|
|
ch->tail->next = newbuf;
|
|
else
|
|
ch->head = ch->tail = newbuf;
|
|
newbuf->next = NULL;
|
|
ch->tail = newbuf;
|
|
}
|
|
}
|
|
|
|
void bufchain_consume(bufchain *ch, int len)
|
|
{
|
|
struct bufchain_granule *tmp;
|
|
|
|
assert(ch->buffersize >= len);
|
|
while (len > 0) {
|
|
int remlen = len;
|
|
assert(ch->head != NULL);
|
|
if (remlen >= ch->head->buflen - ch->head->bufpos) {
|
|
remlen = ch->head->buflen - ch->head->bufpos;
|
|
tmp = ch->head;
|
|
ch->head = tmp->next;
|
|
sfree(tmp);
|
|
if (!ch->head)
|
|
ch->tail = NULL;
|
|
} else
|
|
ch->head->bufpos += remlen;
|
|
ch->buffersize -= remlen;
|
|
len -= remlen;
|
|
}
|
|
}
|
|
|
|
void bufchain_prefix(bufchain *ch, void **data, int *len)
|
|
{
|
|
*len = ch->head->buflen - ch->head->bufpos;
|
|
*data = ch->head->buf + ch->head->bufpos;
|
|
}
|
|
|
|
void bufchain_fetch(bufchain *ch, void *data, int len)
|
|
{
|
|
struct bufchain_granule *tmp;
|
|
char *data_c = (char *)data;
|
|
|
|
tmp = ch->head;
|
|
|
|
assert(ch->buffersize >= len);
|
|
while (len > 0) {
|
|
int remlen = len;
|
|
|
|
assert(tmp != NULL);
|
|
if (remlen >= tmp->buflen - tmp->bufpos)
|
|
remlen = tmp->buflen - tmp->bufpos;
|
|
memcpy(data_c, tmp->buf + tmp->bufpos, remlen);
|
|
|
|
tmp = tmp->next;
|
|
len -= remlen;
|
|
data_c += remlen;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* My own versions of malloc, realloc and free. Because I want
|
|
* malloc and realloc to bomb out and exit the program if they run
|
|
* out of memory, realloc to reliably call malloc if passed a NULL
|
|
* pointer, and free to reliably do nothing if passed a NULL
|
|
* pointer. We can also put trace printouts in, if we need to; and
|
|
* we can also replace the allocator with an ElectricFence-like
|
|
* one.
|
|
*/
|
|
|
|
#ifdef MINEFIELD
|
|
void *minefield_c_malloc(size_t size);
|
|
void minefield_c_free(void *p);
|
|
void *minefield_c_realloc(void *p, size_t size);
|
|
#endif
|
|
|
|
#ifdef MALLOC_LOG
|
|
static FILE *fp = NULL;
|
|
|
|
static char *mlog_file = NULL;
|
|
static int mlog_line = 0;
|
|
|
|
void mlog(char *file, int line)
|
|
{
|
|
mlog_file = file;
|
|
mlog_line = line;
|
|
if (!fp) {
|
|
fp = fopen("putty_mem.log", "w");
|
|
setvbuf(fp, NULL, _IONBF, BUFSIZ);
|
|
}
|
|
if (fp)
|
|
fprintf(fp, "%s:%d: ", file, line);
|
|
}
|
|
#endif
|
|
|
|
void *safemalloc(size_t size)
|
|
{
|
|
void *p;
|
|
#ifdef MINEFIELD
|
|
p = minefield_c_malloc(size);
|
|
#else
|
|
p = malloc(size);
|
|
#endif
|
|
if (!p) {
|
|
char str[200];
|
|
#ifdef MALLOC_LOG
|
|
sprintf(str, "Out of memory! (%s:%d, size=%d)",
|
|
mlog_file, mlog_line, size);
|
|
fprintf(fp, "*** %s\n", str);
|
|
fclose(fp);
|
|
#else
|
|
strcpy(str, "Out of memory!");
|
|
#endif
|
|
modalfatalbox(str);
|
|
}
|
|
#ifdef MALLOC_LOG
|
|
if (fp)
|
|
fprintf(fp, "malloc(%d) returns %p\n", size, p);
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
void *saferealloc(void *ptr, size_t size)
|
|
{
|
|
void *p;
|
|
if (!ptr) {
|
|
#ifdef MINEFIELD
|
|
p = minefield_c_malloc(size);
|
|
#else
|
|
p = malloc(size);
|
|
#endif
|
|
} else {
|
|
#ifdef MINEFIELD
|
|
p = minefield_c_realloc(ptr, size);
|
|
#else
|
|
p = realloc(ptr, size);
|
|
#endif
|
|
}
|
|
if (!p) {
|
|
char str[200];
|
|
#ifdef MALLOC_LOG
|
|
sprintf(str, "Out of memory! (%s:%d, size=%d)",
|
|
mlog_file, mlog_line, size);
|
|
fprintf(fp, "*** %s\n", str);
|
|
fclose(fp);
|
|
#else
|
|
strcpy(str, "Out of memory!");
|
|
#endif
|
|
modalfatalbox(str);
|
|
}
|
|
#ifdef MALLOC_LOG
|
|
if (fp)
|
|
fprintf(fp, "realloc(%p,%d) returns %p\n", ptr, size, p);
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
void safefree(void *ptr)
|
|
{
|
|
if (ptr) {
|
|
#ifdef MALLOC_LOG
|
|
if (fp)
|
|
fprintf(fp, "free(%p)\n", ptr);
|
|
#endif
|
|
#ifdef MINEFIELD
|
|
minefield_c_free(ptr);
|
|
#else
|
|
free(ptr);
|
|
#endif
|
|
}
|
|
#ifdef MALLOC_LOG
|
|
else if (fp)
|
|
fprintf(fp, "freeing null pointer - no action taken\n");
|
|
#endif
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* Debugging routines.
|
|
*/
|
|
|
|
#ifdef DEBUG
|
|
extern void dputs(char *); /* defined in per-platform *misc.c */
|
|
|
|
void debug_printf(char *fmt, ...)
|
|
{
|
|
char *buf;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
buf = dupvprintf(fmt, ap);
|
|
dputs(buf);
|
|
sfree(buf);
|
|
va_end(ap);
|
|
}
|
|
|
|
|
|
void debug_memdump(void *buf, int len, int L)
|
|
{
|
|
int i;
|
|
unsigned char *p = buf;
|
|
char foo[17];
|
|
if (L) {
|
|
int delta;
|
|
debug_printf("\t%d (0x%x) bytes:\n", len, len);
|
|
delta = 15 & (int) p;
|
|
p -= delta;
|
|
len += delta;
|
|
}
|
|
for (; 0 < len; p += 16, len -= 16) {
|
|
dputs(" ");
|
|
if (L)
|
|
debug_printf("%p: ", p);
|
|
strcpy(foo, "................"); /* sixteen dots */
|
|
for (i = 0; i < 16 && i < len; ++i) {
|
|
if (&p[i] < (unsigned char *) buf) {
|
|
dputs(" "); /* 3 spaces */
|
|
foo[i] = ' ';
|
|
} else {
|
|
debug_printf("%c%02.2x",
|
|
&p[i] != (unsigned char *) buf
|
|
&& i % 4 ? '.' : ' ', p[i]
|
|
);
|
|
if (p[i] >= ' ' && p[i] <= '~')
|
|
foo[i] = (char) p[i];
|
|
}
|
|
}
|
|
foo[i] = '\0';
|
|
debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo);
|
|
}
|
|
}
|
|
|
|
#endif /* def DEBUG */
|