Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c3398cbb7 | ||
|
|
2d0a377626 | ||
|
|
232c3b29fd | ||
|
|
3bab5a6151 | ||
|
|
9bb4b61196 | ||
|
|
80597e340d | ||
|
|
c558736424 | ||
|
|
7f7fac22e9 | ||
|
|
8124638aba | ||
|
|
63053a885f | ||
|
|
464c88d396 | ||
|
|
6af39c95c2 | ||
|
|
ebb5e2f164 | ||
|
|
c4b6dabb28 | ||
|
|
e85550c70e | ||
|
|
9d80c6b171 | ||
|
|
9a8af62267 | ||
|
|
45c65bbdb0 | ||
|
|
12d8132e56 | ||
|
|
b76939b3d0 | ||
|
|
659bc96519 | ||
|
|
cb833b241a | ||
|
|
042b9f5cef | ||
|
|
1d13ae9175 | ||
|
|
2e87a6dc2b | ||
|
|
057faa84f9 | ||
|
|
8612fa5ef2 | ||
|
|
0ae14d0859 | ||
|
|
977da5f49c | ||
|
|
cc43da0078 | ||
|
|
7400667711 | ||
|
|
b9f427b176 | ||
|
|
f1377e7040 | ||
|
|
62c2de1dd2 | ||
|
|
5180166b5d | ||
|
|
e45c98b427 | ||
|
|
dea7220469 | ||
|
|
49d45ac099 | ||
|
|
a38fa17431 | ||
|
|
4c9cecbe68 | ||
|
|
8312a7eece | ||
|
|
752f1e9584 | ||
|
|
3891b2546f | ||
|
|
23f20ba6ef | ||
|
|
235d66ccc8 |
@@ -0,0 +1,2 @@
|
||||
github: [ cyd01 ]
|
||||
custom: [ "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=65VYL8F5AD57G&source=url", "https://www.9bis.com/kitty/" ]
|
||||
+10
-12
@@ -3,17 +3,17 @@ name: C/C++ CI
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/heads
|
||||
branches:
|
||||
- master # Push events on master branch
|
||||
- feat-*
|
||||
- fix-*
|
||||
- bugfix*
|
||||
# branches:
|
||||
# - master # Push events on master branch
|
||||
# - feat-*
|
||||
# - fix-*
|
||||
# - bugfix*
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- v*
|
||||
|
||||
env:
|
||||
VERSION: 0.73
|
||||
VERSION: 0.74
|
||||
|
||||
jobs:
|
||||
# Build for Win 32bits
|
||||
@@ -32,15 +32,14 @@ jobs:
|
||||
mkdir ../builds
|
||||
docker run --rm -i -v $(pwd)/../builds:/builds -v $(pwd):/sources cyd01/cross-gcc "cd ${VERSION}_My_PuTTY/windows ; make -f MAKEFILE.MINGW cross ; cd /builds ; ls -l"
|
||||
mv -f $(pwd)/../builds ./
|
||||
cd builds ; zip kitty.zip *.exe
|
||||
|
||||
# Upload runner package tar.gz/zip as artifact
|
||||
- name: Publish Artifact
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: kitty.zip
|
||||
path: builds/kitty.zip
|
||||
name: kitty
|
||||
path: builds/
|
||||
|
||||
# Build for Win 64bits
|
||||
build64:
|
||||
@@ -57,12 +56,11 @@ jobs:
|
||||
mkdir ../builds
|
||||
docker run --rm -i -v $(pwd)/../builds:/builds -v $(pwd):/sources cyd01/cross-gcc "cd ${VERSION}_My_PuTTY/windows ; make -f MAKEFILE.MINGW cross64 ; cd /builds ; ls -l"
|
||||
mv -f $(pwd)/../builds ./
|
||||
cd builds ; zip kitty.zip *.exe
|
||||
|
||||
# Upload runner package tar.gz/zip as artifact
|
||||
- name: Publish Artifact
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: kitty64.zip
|
||||
path: builds/kitty.zip
|
||||
name: kitty64
|
||||
path: builds/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
||||
#define RELEASE 0.73
|
||||
#define TEXTVER "Release 0.73"
|
||||
#define SSHVER "-Release-0.73"
|
||||
#define BINARY_VERSION 0,73,2,14
|
||||
#define SOURCE_COMMIT "unavailable"
|
||||
@@ -1 +0,0 @@
|
||||
"0.73.2"
|
||||
@@ -1 +0,0 @@
|
||||
14
|
||||
@@ -1,450 +0,0 @@
|
||||
/*
|
||||
* Serial back end (Windows-specific).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
#define SERIAL_MAX_BACKLOG 4096
|
||||
|
||||
typedef struct Serial Serial;
|
||||
struct Serial {
|
||||
HANDLE port;
|
||||
struct handle *out, *in;
|
||||
Seat *seat;
|
||||
LogContext *logctx;
|
||||
int bufsize;
|
||||
long clearbreak_time;
|
||||
bool break_in_progress;
|
||||
Backend backend;
|
||||
};
|
||||
|
||||
static void serial_terminate(Serial *serial)
|
||||
{
|
||||
if (serial->out) {
|
||||
handle_free(serial->out);
|
||||
serial->out = NULL;
|
||||
}
|
||||
if (serial->in) {
|
||||
handle_free(serial->in);
|
||||
serial->in = NULL;
|
||||
}
|
||||
if (serial->port != INVALID_HANDLE_VALUE) {
|
||||
if (serial->break_in_progress)
|
||||
ClearCommBreak(serial->port);
|
||||
CloseHandle(serial->port);
|
||||
serial->port = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t serial_gotdata(
|
||||
struct handle *h, const void *data, size_t len, int err)
|
||||
{
|
||||
Serial *serial = (Serial *)handle_get_privdata(h);
|
||||
if (err || len == 0) {
|
||||
const char *error_msg;
|
||||
|
||||
/*
|
||||
* Currently, len==0 should never happen because we're
|
||||
* ignoring EOFs. However, it seems not totally impossible
|
||||
* that this same back end might be usable to talk to named
|
||||
* pipes or some other non-serial device, in which case EOF
|
||||
* may become meaningful here.
|
||||
*/
|
||||
if (!err)
|
||||
error_msg = "End of file reading from serial device";
|
||||
else
|
||||
error_msg = "Error reading from serial device";
|
||||
|
||||
serial_terminate(serial);
|
||||
|
||||
seat_notify_remote_exit(serial->seat);
|
||||
|
||||
logevent(serial->logctx, error_msg);
|
||||
|
||||
seat_connection_fatal(serial->seat, "%s", error_msg);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return seat_stdout(serial->seat, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void serial_sentdata(struct handle *h, size_t new_backlog, int err)
|
||||
{
|
||||
Serial *serial = (Serial *)handle_get_privdata(h);
|
||||
if (err) {
|
||||
const char *error_msg = "Error writing to serial device";
|
||||
|
||||
serial_terminate(serial);
|
||||
|
||||
seat_notify_remote_exit(serial->seat);
|
||||
|
||||
logevent(serial->logctx, error_msg);
|
||||
|
||||
seat_connection_fatal(serial->seat, "%s", error_msg);
|
||||
} else {
|
||||
serial->bufsize = new_backlog;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *serial_configure(Serial *serial, HANDLE serport, Conf *conf)
|
||||
{
|
||||
DCB dcb;
|
||||
COMMTIMEOUTS timeouts;
|
||||
|
||||
/*
|
||||
* Set up the serial port parameters. If we can't even
|
||||
* GetCommState, we ignore the problem on the grounds that the
|
||||
* user might have pointed us at some other type of two-way
|
||||
* device instead of a serial port.
|
||||
*/
|
||||
if (GetCommState(serport, &dcb)) {
|
||||
const char *str;
|
||||
|
||||
/*
|
||||
* Boilerplate.
|
||||
*/
|
||||
dcb.fBinary = true;
|
||||
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
dcb.fDsrSensitivity = false;
|
||||
dcb.fTXContinueOnXoff = false;
|
||||
dcb.fOutX = false;
|
||||
dcb.fInX = false;
|
||||
dcb.fErrorChar = false;
|
||||
dcb.fNull = false;
|
||||
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
dcb.fAbortOnError = false;
|
||||
dcb.fOutxCtsFlow = false;
|
||||
dcb.fOutxDsrFlow = false;
|
||||
|
||||
/*
|
||||
* Configurable parameters.
|
||||
*/
|
||||
dcb.BaudRate = conf_get_int(conf, CONF_serspeed);
|
||||
logeventf(serial->logctx, "Configuring baud rate %lu", dcb.BaudRate);
|
||||
|
||||
dcb.ByteSize = conf_get_int(conf, CONF_serdatabits);
|
||||
logeventf(serial->logctx, "Configuring %u data bits", dcb.ByteSize);
|
||||
|
||||
switch (conf_get_int(conf, CONF_serstopbits)) {
|
||||
case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break;
|
||||
case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break;
|
||||
case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break;
|
||||
default: return "Invalid number of stop bits (need 1, 1.5 or 2)";
|
||||
}
|
||||
logeventf(serial->logctx, "Configuring %s data bits", str);
|
||||
|
||||
switch (conf_get_int(conf, CONF_serparity)) {
|
||||
case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break;
|
||||
case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break;
|
||||
case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break;
|
||||
case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break;
|
||||
case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break;
|
||||
}
|
||||
logeventf(serial->logctx, "Configuring %s parity", str);
|
||||
|
||||
switch (conf_get_int(conf, CONF_serflow)) {
|
||||
case SER_FLOW_NONE:
|
||||
str = "no";
|
||||
break;
|
||||
case SER_FLOW_XONXOFF:
|
||||
dcb.fOutX = dcb.fInX = true;
|
||||
str = "XON/XOFF";
|
||||
break;
|
||||
case SER_FLOW_RTSCTS:
|
||||
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
|
||||
dcb.fOutxCtsFlow = true;
|
||||
str = "RTS/CTS";
|
||||
break;
|
||||
case SER_FLOW_DSRDTR:
|
||||
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
|
||||
dcb.fOutxDsrFlow = true;
|
||||
str = "DSR/DTR";
|
||||
break;
|
||||
}
|
||||
logeventf(serial->logctx, "Configuring %s flow control", str);
|
||||
|
||||
if (!SetCommState(serport, &dcb))
|
||||
return "Unable to configure serial port";
|
||||
|
||||
timeouts.ReadIntervalTimeout = 1;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 0;
|
||||
timeouts.ReadTotalTimeoutConstant = 0;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 0;
|
||||
timeouts.WriteTotalTimeoutConstant = 0;
|
||||
if (!SetCommTimeouts(serport, &timeouts))
|
||||
return "Unable to configure serial timeouts";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to set up the serial connection.
|
||||
*
|
||||
* Returns an error message, or NULL on success.
|
||||
*
|
||||
* Also places the canonical host name into `realhost'. It must be
|
||||
* freed by the caller.
|
||||
*/
|
||||
static const char *serial_init(Seat *seat, Backend **backend_handle,
|
||||
LogContext *logctx, Conf *conf,
|
||||
const char *host, int port,
|
||||
char **realhost, bool nodelay, bool keepalive)
|
||||
{
|
||||
Serial *serial;
|
||||
HANDLE serport;
|
||||
const char *err;
|
||||
char *serline;
|
||||
|
||||
/* No local authentication phase in this protocol */
|
||||
seat_set_trust_status(seat, false);
|
||||
|
||||
serial = snew(Serial);
|
||||
serial->port = INVALID_HANDLE_VALUE;
|
||||
serial->out = serial->in = NULL;
|
||||
serial->bufsize = 0;
|
||||
serial->break_in_progress = false;
|
||||
serial->backend.vt = &serial_backend;
|
||||
*backend_handle = &serial->backend;
|
||||
|
||||
serial->seat = seat;
|
||||
serial->logctx = logctx;
|
||||
|
||||
serline = conf_get_str(conf, CONF_serline);
|
||||
logeventf(serial->logctx, "Opening serial device %s", serline);
|
||||
|
||||
{
|
||||
/*
|
||||
* Munge the string supplied by the user into a Windows filename.
|
||||
*
|
||||
* Windows supports opening a few "legacy" devices (including
|
||||
* COM1-9) by specifying their names verbatim as a filename to
|
||||
* open. (Thus, no files can ever have these names. See
|
||||
* <http://msdn2.microsoft.com/en-us/library/aa365247.aspx>
|
||||
* ("Naming a File") for the complete list of reserved names.)
|
||||
*
|
||||
* However, this doesn't let you get at devices COM10 and above.
|
||||
* For that, you need to specify a filename like "\\.\COM10".
|
||||
* This is also necessary for special serial and serial-like
|
||||
* devices such as \\.\WCEUSBSH001. It also works for the "legacy"
|
||||
* names, so you can do \\.\COM1 (verified as far back as Win95).
|
||||
* See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx>
|
||||
* (CreateFile() docs).
|
||||
*
|
||||
* So, we believe that prepending "\\.\" should always be the
|
||||
* Right Thing. However, just in case someone finds something to
|
||||
* talk to that doesn't exist under there, if the serial line
|
||||
* contains a backslash, we use it verbatim. (This also lets
|
||||
* existing configurations using \\.\ continue working.)
|
||||
*/
|
||||
char *serfilename =
|
||||
dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline);
|
||||
serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
sfree(serfilename);
|
||||
}
|
||||
|
||||
if (serport == INVALID_HANDLE_VALUE)
|
||||
return "Unable to open serial port";
|
||||
|
||||
err = serial_configure(serial, serport, conf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
serial->port = serport;
|
||||
serial->out = handle_output_new(serport, serial_sentdata, serial,
|
||||
HANDLE_FLAG_OVERLAPPED);
|
||||
serial->in = handle_input_new(serport, serial_gotdata, serial,
|
||||
HANDLE_FLAG_OVERLAPPED |
|
||||
HANDLE_FLAG_IGNOREEOF |
|
||||
HANDLE_FLAG_UNITBUFFER);
|
||||
|
||||
*realhost = dupstr(serline);
|
||||
|
||||
/*
|
||||
* Specials are always available.
|
||||
*/
|
||||
seat_update_specials_menu(serial->seat);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void serial_free(Backend *be)
|
||||
{
|
||||
Serial *serial = container_of(be, Serial, backend);
|
||||
|
||||
serial_terminate(serial);
|
||||
expire_timer_context(serial);
|
||||
sfree(serial);
|
||||
}
|
||||
|
||||
static void serial_reconfig(Backend *be, Conf *conf)
|
||||
{
|
||||
Serial *serial = container_of(be, Serial, backend);
|
||||
|
||||
serial_configure(serial, serial->port, conf);
|
||||
|
||||
/*
|
||||
* FIXME: what should we do if that call returned a non-NULL error
|
||||
* message?
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to send data down the serial connection.
|
||||
*/
|
||||
static size_t serial_send(Backend *be, const char *buf, size_t len)
|
||||
{
|
||||
Serial *serial = container_of(be, Serial, backend);
|
||||
|
||||
if (serial->out == NULL)
|
||||
return 0;
|
||||
|
||||
serial->bufsize = handle_write(serial->out, buf, len);
|
||||
return serial->bufsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to query the current sendability status.
|
||||
*/
|
||||
static size_t serial_sendbuffer(Backend *be)
|
||||
{
|
||||
Serial *serial = container_of(be, Serial, backend);
|
||||
return serial->bufsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to set the size of the window
|
||||
*/
|
||||
static void serial_size(Backend *be, int width, int height)
|
||||
{
|
||||
/* Do nothing! */
|
||||
return;
|
||||
}
|
||||
|
||||
static void serbreak_timer(void *ctx, unsigned long now)
|
||||
{
|
||||
Serial *serial = (Serial *)ctx;
|
||||
|
||||
if (now == serial->clearbreak_time && serial->port) {
|
||||
ClearCommBreak(serial->port);
|
||||
serial->break_in_progress = false;
|
||||
logevent(serial->logctx, "Finished serial break");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Send serial special codes.
|
||||
*/
|
||||
static void serial_special(Backend *be, SessionSpecialCode code, int arg)
|
||||
{
|
||||
Serial *serial = container_of(be, Serial, backend);
|
||||
|
||||
if (serial->port && code == SS_BRK) {
|
||||
logevent(serial->logctx, "Starting serial break at user request");
|
||||
SetCommBreak(serial->port);
|
||||
/*
|
||||
* To send a serial break on Windows, we call SetCommBreak
|
||||
* to begin the break, then wait a bit, and then call
|
||||
* ClearCommBreak to finish it. Hence, I must use timing.c
|
||||
* to arrange a callback when it's time to do the latter.
|
||||
*
|
||||
* SUS says that a default break length must be between 1/4
|
||||
* and 1/2 second. FreeBSD apparently goes with 2/5 second,
|
||||
* and so will I.
|
||||
*/
|
||||
serial->clearbreak_time =
|
||||
schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial);
|
||||
serial->break_in_progress = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a list of the special codes that make sense in this
|
||||
* protocol.
|
||||
*/
|
||||
static const SessionSpecial *serial_get_specials(Backend *be)
|
||||
{
|
||||
static const SessionSpecial specials[] = {
|
||||
{"Break", SS_BRK},
|
||||
{NULL, SS_EXITMENU}
|
||||
};
|
||||
return specials;
|
||||
}
|
||||
|
||||
static bool serial_connected(Backend *be)
|
||||
{
|
||||
return true; /* always connected */
|
||||
}
|
||||
|
||||
static bool serial_sendok(Backend *be)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void serial_unthrottle(Backend *be, size_t backlog)
|
||||
{
|
||||
Serial *serial = container_of(be, Serial, backend);
|
||||
if (serial->in)
|
||||
handle_unthrottle(serial->in, backlog);
|
||||
}
|
||||
|
||||
static bool serial_ldisc(Backend *be, int option)
|
||||
{
|
||||
/*
|
||||
* Local editing and local echo are off by default.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
static void serial_provide_ldisc(Backend *be, Ldisc *ldisc)
|
||||
{
|
||||
/* This is a stub. */
|
||||
}
|
||||
|
||||
static int serial_exitcode(Backend *be)
|
||||
{
|
||||
Serial *serial = container_of(be, Serial, backend);
|
||||
if (serial->port != INVALID_HANDLE_VALUE)
|
||||
return -1; /* still connected */
|
||||
else
|
||||
/* Exit codes are a meaningless concept with serial ports */
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
/*
|
||||
* cfg_info for Serial does nothing at all.
|
||||
*/
|
||||
static int serial_cfg_info(Backend *be)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct BackendVtable serial_backend = {
|
||||
serial_init,
|
||||
serial_free,
|
||||
serial_reconfig,
|
||||
serial_send,
|
||||
serial_sendbuffer,
|
||||
serial_size,
|
||||
serial_special,
|
||||
serial_get_specials,
|
||||
serial_connected,
|
||||
serial_exitcode,
|
||||
serial_sendok,
|
||||
serial_ldisc,
|
||||
serial_provide_ldisc,
|
||||
serial_unthrottle,
|
||||
serial_cfg_info,
|
||||
NULL /* test_for_upstream */,
|
||||
"serial",
|
||||
PROT_SERIAL,
|
||||
0
|
||||
};
|
||||
@@ -14,7 +14,7 @@
|
||||
* have tiny little source modules containing nothing but
|
||||
* declarations of appname, for as long as I can...
|
||||
*/
|
||||
#if (defined MOD_PERSO) && (!defined FDJ)
|
||||
#if (defined MOD_PERSO) && (!defined FLJ)
|
||||
char *appname = "KiTTY";
|
||||
#else
|
||||
const char *const appname = "PuTTY";
|
||||
@@ -53,10 +53,10 @@ int console_get_userpass_input(prompts_t *p)
|
||||
int ret = 1;
|
||||
for (i = 0; i < p->n_prompts; i++) {
|
||||
if (promptsgot < nprompts) {
|
||||
p->prompts[i]->result = dupstr(prompts[promptsgot++]);
|
||||
prompt_set_result(p->prompts[i], prompts[promptsgot++]);
|
||||
if (cgtest_verbose)
|
||||
printf(" prompt \"%s\": response \"%s\"\n",
|
||||
p->prompts[i]->prompt, p->prompts[i]->result);
|
||||
p->prompts[i]->prompt, p->prompts[i]->result->s);
|
||||
} else {
|
||||
promptsgot++; /* track number of requests anyway */
|
||||
ret = 0;
|
||||
@@ -486,7 +486,7 @@ int main(int argc, char **argv)
|
||||
bits = 384;
|
||||
break;
|
||||
case ED25519:
|
||||
bits = 256;
|
||||
bits = 255;
|
||||
break;
|
||||
default:
|
||||
bits = DEFAULT_RSADSA_BITS;
|
||||
@@ -499,8 +499,8 @@ int main(int argc, char **argv)
|
||||
errs = true;
|
||||
}
|
||||
|
||||
if (keytype == ED25519 && (bits != 256)) {
|
||||
fprintf(stderr, "puttygen: invalid bits for ED25519, choose 256\n");
|
||||
if (keytype == ED25519 && (bits != 255) && (bits != 256)) {
|
||||
fprintf(stderr, "puttygen: invalid bits for ED25519, choose 255\n");
|
||||
errs = true;
|
||||
}
|
||||
|
||||
@@ -619,7 +619,7 @@ int main(int argc, char **argv)
|
||||
(intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) {
|
||||
if (!outfile) {
|
||||
outfile = infile;
|
||||
outfiletmp = dupcat(outfile, ".tmp", NULL);
|
||||
outfiletmp = dupcat(outfile, ".tmp");
|
||||
}
|
||||
|
||||
if (!change_passphrase && !comment) {
|
||||
@@ -774,7 +774,7 @@ int main(int argc, char **argv)
|
||||
perror("puttygen: unable to read passphrase");
|
||||
return 1;
|
||||
} else {
|
||||
old_passphrase = dupstr(p->prompts[0]->result);
|
||||
old_passphrase = prompt_get_result(p->prompts[0]);
|
||||
free_prompts(p);
|
||||
}
|
||||
}
|
||||
@@ -903,7 +903,7 @@ int main(int argc, char **argv)
|
||||
* we have just generated a key.
|
||||
*/
|
||||
if (!new_passphrase && (change_passphrase || keytype != NOKEYGEN)) {
|
||||
prompts_t *p = new_prompts(NULL);
|
||||
prompts_t *p = new_prompts();
|
||||
int ret;
|
||||
|
||||
p->to_server = false;
|
||||
@@ -918,12 +918,13 @@ int main(int argc, char **argv)
|
||||
perror("puttygen: unable to read new passphrase");
|
||||
return 1;
|
||||
} else {
|
||||
if (strcmp(p->prompts[0]->result, p->prompts[1]->result)) {
|
||||
if (strcmp(prompt_get_result_ref(p->prompts[0]),
|
||||
prompt_get_result_ref(p->prompts[1]))) {
|
||||
free_prompts(p);
|
||||
fprintf(stderr, "puttygen: passphrases do not match\n");
|
||||
return 1;
|
||||
}
|
||||
new_passphrase = dupstr(p->prompts[0]->result);
|
||||
new_passphrase = prompt_get_result(p->prompts[0]);
|
||||
free_prompts(p);
|
||||
}
|
||||
}
|
||||
@@ -1194,7 +1195,7 @@ void test(int retval, ...)
|
||||
sfree(argv);
|
||||
}
|
||||
|
||||
void filecmp(char *file1, char *file2, char *fmt, ...)
|
||||
PRINTF_LIKE(3, 4) void filecmp(char *file1, char *file2, char *fmt, ...)
|
||||
{
|
||||
/*
|
||||
* Ideally I should do file comparison myself, to maximise the
|
||||
@@ -1259,7 +1260,7 @@ char *get_fp(char *filename)
|
||||
return cleanup_fp(buf);
|
||||
}
|
||||
|
||||
void check_fp(char *filename, char *fp, char *fmt, ...)
|
||||
PRINTF_LIKE(3, 4) void check_fp(char *filename, char *fp, char *fmt, ...)
|
||||
{
|
||||
char *newfp;
|
||||
|
||||
@@ -1335,7 +1336,8 @@ int main(int argc, char **argv)
|
||||
pubfilename, tmpfilename1);
|
||||
if (system(cmdbuf) ||
|
||||
(fp = get_fp(tmpfilename1)) == NULL) {
|
||||
printf("UNABLE to test fingerprint matching against OpenSSH");
|
||||
printf("UNABLE to test fingerprint matching against "
|
||||
"OpenSSH\n");
|
||||
}
|
||||
sfree(cmdbuf);
|
||||
}
|
||||
@@ -1679,7 +1681,7 @@ int main(int argc, char **argv)
|
||||
test(1, "puttygen", "-C", "spurious-new-comment", pubfilename, NULL);
|
||||
}
|
||||
printf("%d passes, %d fails\n", passes, fails);
|
||||
return 0;
|
||||
return fails == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -30,9 +30,93 @@
|
||||
|
||||
#ifdef MOD_PERSO
|
||||
#include "kitty_crypt.h"
|
||||
#include "kitty.h"
|
||||
int decode64(char *buffer) ;
|
||||
void SetAutoStoreSSHKey( void ) ;
|
||||
void load_open_settings_forced(char *filename, Conf *conf) ;
|
||||
void SetPasswordInConfig( const char * password ) ;
|
||||
int GetCryptSaltFlag() ;
|
||||
extern char CurrentFolder[1024] ;
|
||||
// Manage connect string as: user:pass@@@hostname:port/cmd
|
||||
void ManageConnectString( Conf *cf, char * hostname ) {
|
||||
int i, j ;
|
||||
char *p, *us, *pw, *hn, *po, *cm ;
|
||||
us = (char*)malloc(strlen(hostname)+1); strcpy(us,"");
|
||||
pw = (char*)malloc(strlen(hostname)+1); strcpy(pw,"");
|
||||
hn = (char*)malloc(strlen(hostname)+1); strcpy(hn,hostname);
|
||||
po = (char*)malloc(strlen(hostname)+1); strcpy(po,"");
|
||||
cm = (char*)malloc(strlen(hostname)+1); strcpy(cm,"");
|
||||
for( i=0 ; i<strlen(hostname) ; i++ ) {
|
||||
if( hostname[i]=='@' ) {
|
||||
if( hostname[i+1]=='@' ) {
|
||||
i++ ;
|
||||
} else {
|
||||
strcpy( us, hostname ) ;
|
||||
us[i] = '\0' ;
|
||||
j = 0 ;
|
||||
do { i++; hn[j]=hostname[i]; j++; } while( hostname[i]!='\0' ) ;
|
||||
break ;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( hn[0]=='[' ) { // IPv6
|
||||
char *q ;
|
||||
if( (q = strstr( hn, "]" )) != NULL ) {
|
||||
q++ ;
|
||||
if( (p = strstr( q, "/" )) != NULL ) {
|
||||
strcpy( cm, p+1 ) ;
|
||||
p[0] = '\0' ;
|
||||
}
|
||||
if( (p = strstr( q, ":" )) != NULL ) {
|
||||
strcpy( po, p+1 ) ;
|
||||
p[0] = '\0' ;
|
||||
}
|
||||
q[0] = '\0' ;
|
||||
}
|
||||
} else { // IPv4
|
||||
if( (p = strstr( hn, "/" )) != NULL ) {
|
||||
strcpy( cm, p+1 ) ;
|
||||
p[0] = '\0' ;
|
||||
}
|
||||
if( (p = strstr( hn, ":" )) != NULL ) {
|
||||
strcpy( po, p+1 ) ;
|
||||
p[0] = '\0' ;
|
||||
}
|
||||
|
||||
}
|
||||
if( strlen(us)>0 ) {
|
||||
if( (p = strstr( us, ":" )) != NULL ) {
|
||||
strcpy( pw, p+1 ) ;
|
||||
p[0] = '\0' ;
|
||||
while( (p = strstr( pw, "@@" )) != NULL ) { // Manage @ in password ( @ => double @@)
|
||||
do { p[0] = p[1] ; p++ ; } while( p[0] != '\0' ) ;
|
||||
}
|
||||
}
|
||||
if( strlen(pw)>0 ) {
|
||||
SetPasswordInConfig( pw ) ;
|
||||
}
|
||||
sprintf( hostname, "%s@%s", us, hn ) ;
|
||||
} else {
|
||||
strcpy( hostname, hn ) ;
|
||||
}
|
||||
if( strlen(po)>0 ) {
|
||||
conf_set_int( cf, CONF_port, atoi(po) ) ;
|
||||
}
|
||||
if( strlen(cm)>0 ) {
|
||||
if( cm[0] == '#' ) {
|
||||
decryptstring( GetCryptSaltFlag(), (char*)cm+1, MASTER_PASSWORD ) ;
|
||||
conf_set_str( conf, CONF_autocommand, cm+1 ) ;
|
||||
} else {
|
||||
char *s = (char*)malloc( strlen(cm)+1 ) ;
|
||||
strcpy( s, cm ) ;
|
||||
int i = decode64(s) ;
|
||||
s[i] = '\0' ;
|
||||
conf_set_str( conf,CONF_autocommand,s ) ;
|
||||
free(s) ;
|
||||
}
|
||||
}
|
||||
free(cm);free(po);free(hn);free(pw);free(us);
|
||||
}
|
||||
#endif
|
||||
#ifdef MOD_ADB
|
||||
int GetADBFlag(void) ;
|
||||
@@ -172,7 +256,6 @@ int cmdline_process_param(const char *p, char *value,
|
||||
int need_save, Conf *conf)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (p[0] != '-') {
|
||||
if (need_save < 0)
|
||||
return 0;
|
||||
@@ -207,6 +290,7 @@ int cmdline_process_param(const char *p, char *value,
|
||||
* the default session and never be able to do anything
|
||||
* else).
|
||||
*/
|
||||
|
||||
if (!strncmp(p, "telnet:", 7)) {
|
||||
/*
|
||||
* If the argument starts with "telnet:", set the
|
||||
@@ -254,59 +338,26 @@ int cmdline_process_param(const char *p, char *value,
|
||||
conf_set_int(conf, CONF_port, -1);
|
||||
}
|
||||
#ifdef MOD_PERSO
|
||||
} else if (!strncmp(p, "ssh:", 4)) {
|
||||
} else if (!strncmp(p, "ssh:", 4)) {
|
||||
char *pst = (char*)malloc(strlen(p)+1) ; strcpy(pst,p);
|
||||
char *q = (char*)pst ;
|
||||
/*
|
||||
* If the hostname starts with "ssh:",
|
||||
* set the protocol to SSH and process
|
||||
* the string as a SSH URL
|
||||
*/
|
||||
char c;
|
||||
q += 4;
|
||||
if (q[0] == '/' && q[1] == '/')
|
||||
q += 2;
|
||||
conf_set_int( conf, CONF_protocol, PROT_SSH);
|
||||
pst = q;
|
||||
while (*pst && *pst != ':' && *pst != '/')
|
||||
pst++;
|
||||
c = *pst;
|
||||
/*
|
||||
* If the hostname starts with "ssh:",
|
||||
* set the protocol to SSH and process
|
||||
* the string as a SSH URL
|
||||
*/
|
||||
q += 4;
|
||||
if (q[0] == '/' && q[1] == '/')
|
||||
q += 2;
|
||||
//while (*q && *q != ':' && *q != '/') q++ ;
|
||||
conf_set_int( conf, CONF_protocol, PROT_SSH);
|
||||
conf_set_int( conf, CONF_port, 22);
|
||||
ManageConnectString( conf, q ) ;
|
||||
|
||||
if (*pst) *pst++ = '\0';
|
||||
|
||||
if (c == ':')
|
||||
conf_set_int( conf,CONF_port,atoi(pst));
|
||||
else if( (c == '/')&&(strlen(pst)>0) ) {
|
||||
conf_set_int( conf,CONF_port,22);
|
||||
char * buf;
|
||||
buf=(char*)malloc(strlen(pst)+10);
|
||||
strcpy(buf,pst);
|
||||
if( pst[0]=='#' ) {
|
||||
decryptstring( (char*)pst+1, MASTER_PASSWORD ) ;
|
||||
conf_set_str( conf,CONF_autocommand, pst+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *s = (char*)malloc( strlen(pst)+1 ) ;
|
||||
strcpy( s, pst ) ;
|
||||
int i = decode64(s) ;
|
||||
s[i]='\0';
|
||||
conf_set_str(conf,CONF_autocommand,s);
|
||||
free(s);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
else
|
||||
conf_set_int( conf,CONF_port,22) ;
|
||||
char * buf;
|
||||
buf=(char*)malloc( strlen(q)+10 );
|
||||
strncpy(buf,q,strlen(q)+1);
|
||||
buf[strlen(q)+1] = '\0' ;
|
||||
conf_set_str( conf, CONF_host, buf);
|
||||
free(buf);
|
||||
seen_hostname_argument = true ;
|
||||
conf_set_str( conf, CONF_host, q ) ;
|
||||
seen_hostname_argument = true ;
|
||||
free(pst);
|
||||
} else if (!strncmp(p, "putty:", 4)) {
|
||||
} else if (!strncmp(p, "putty:", 4)) {
|
||||
char * q = (char*)p ;
|
||||
int ret = 0;
|
||||
q += 6;
|
||||
@@ -392,6 +443,10 @@ int cmdline_process_param(const char *p, char *value,
|
||||
hostname[len-1] == '\t'))
|
||||
hostname[--len] = '\0';
|
||||
seen_hostname_argument = true;
|
||||
#ifdef MOD_PERSO
|
||||
// Manage connect string user:pass@hostname
|
||||
ManageConnectString( conf, hostname ) ;
|
||||
#endif
|
||||
conf_set_str(conf, CONF_host, hostname);
|
||||
|
||||
if ((cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) &&
|
||||
@@ -470,9 +525,36 @@ int cmdline_process_param(const char *p, char *value,
|
||||
RETURN(2);
|
||||
/* This parameter must be processed immediately rather than being
|
||||
* saved. */
|
||||
#if MOD_PERSO
|
||||
if( IniFileFlag == SAVEMODE_REG ) {
|
||||
do_defaults(value, conf);
|
||||
loaded_session = true;
|
||||
cmdline_session_name = dupstr(value) ;
|
||||
} else {
|
||||
char *pst = strstr( value, "/" ) ;
|
||||
if( pst==NULL ) {
|
||||
do_defaults(value, conf);
|
||||
loaded_session = true;
|
||||
cmdline_session_name = dupstr(value) ;
|
||||
} else {
|
||||
char *name = (char*)malloc( strlen(value)+1 ) ;
|
||||
strcpy(name,value) ;
|
||||
pst = strstr( name, "/" ) ;
|
||||
cmdline_session_name = dupstr(pst+1) ;
|
||||
pst[0]='\0';
|
||||
conf_set_str(conf,CONF_folder,name) ;
|
||||
SetSessPath( name ) ;
|
||||
strcpy( CurrentFolder, name ) ;
|
||||
do_defaults(pst+1, conf);
|
||||
loaded_session = true;
|
||||
free(value) ;
|
||||
}
|
||||
}
|
||||
#else
|
||||
do_defaults(value, conf);
|
||||
loaded_session = true;
|
||||
cmdline_session_name = dupstr(value);
|
||||
#endif
|
||||
return 2;
|
||||
}
|
||||
if (!strcmp(p, "-ssh")) {
|
||||
@@ -522,6 +604,10 @@ int cmdline_process_param(const char *p, char *value,
|
||||
cmdline_session_name = dupstr(value);
|
||||
return 2;
|
||||
}
|
||||
if (!strcmp(p, "-knock")) {
|
||||
RETURN(2);
|
||||
conf_set_str( conf, CONF_portknockingoptions, value ) ;
|
||||
}
|
||||
if ( !strcmp(p, "-auto_store_sshkey") || !strcmp(p, "-auto-store-sshkey") ) {
|
||||
RETURN(1);
|
||||
SetAutoStoreSSHKey();
|
||||
@@ -15,8 +15,9 @@
|
||||
#endif /* rutty */
|
||||
#ifdef MOD_PERSO
|
||||
#include "kitty.h"
|
||||
#include "kitty_store.h"
|
||||
union control * ctrlHostnameEdit = NULL ;
|
||||
void MASKPASS( char * password ) ;
|
||||
void MASKPASS( const int mode, char * password ) ;
|
||||
int stricmp(const char *s1, const char *s2) ;
|
||||
int GetReadOnlyFlag(void) ;
|
||||
#endif
|
||||
@@ -722,7 +723,7 @@ static void sshbug_handler(union control *ctrl, dlgparam *dlg,
|
||||
int dlg_listbox_get(union control *ctrl, void *dlg, int index, char * pstr, int maxcount) ;
|
||||
int dlg_listbox_gettext(union control *ctrl, void *dlg, int index, char * pstr, int maxcount) ;
|
||||
|
||||
int StringList_Add( char **list, char *str ) ;
|
||||
int StringList_Add( char **list, const char *str ) ;
|
||||
int StringList_Exist( const char **list, const char * name ) ;
|
||||
void StringList_Del( char **list, const char * name ) ;
|
||||
void StringList_Up( char **list, const char * name ) ;
|
||||
@@ -738,7 +739,9 @@ int RunSession( HWND hwnd, const char * folder_in, char * session_in ) ;
|
||||
|
||||
extern char ** FolderList ;
|
||||
|
||||
static char CurrentFolder[1024]="Default" ;
|
||||
static char * selectedsession = NULL ;
|
||||
|
||||
extern char CurrentFolder[1024] ;
|
||||
union control * ctrlSessionList = NULL ;
|
||||
union control * ctrlSessionEdit = NULL ;
|
||||
union control * ctrlFolderList = NULL ;
|
||||
@@ -813,7 +816,7 @@ static void folder_handler(union control *ctrl, dlgparam *dlg,
|
||||
struct sessionsaver_data {
|
||||
#ifdef MOD_PERSO
|
||||
union control *editbox, *listbox, *clearbutton, *loadbutton, *savebutton, *delbutton, *createbutton, *delfolderbutton, *arrangebutton
|
||||
#if (defined MOD_PERSO) && (!defined FDJ) && (defined MOD_STARTBUTTON)
|
||||
#if (defined MOD_PERSO) && (!defined FLJ) && (defined MOD_STARTBUTTON)
|
||||
, *startbutton
|
||||
#endif
|
||||
;
|
||||
@@ -850,6 +853,11 @@ static bool load_selected_session(
|
||||
char sessionname[1024] ;
|
||||
int j;
|
||||
dlg_listbox_gettext(ctrlSessionList, dlg, i, sessionname, 1024 ) ;
|
||||
|
||||
if( selectedsession!=NULL ) { free(selectedsession); }
|
||||
selectedsession = (char*)malloc(strlen(sessionname)+1);
|
||||
strcpy(selectedsession,sessionname);
|
||||
|
||||
for( j=0; j<ssd->sesslist.nsessions; j++ ) {
|
||||
if( !strcmp( ssd->sesslist.sessions[j], sessionname ) ) i = j ;
|
||||
}
|
||||
@@ -983,13 +991,14 @@ void sessionlist_add( char** sessionslist, const char* val, int* pos, bool check
|
||||
}
|
||||
}
|
||||
|
||||
void filter_session_portable(union control *ctrl, dlgparam *dlg, const int nb, const char** sessionslist, const char * savedsession) {
|
||||
void filter_session_portable(union control *ctrl, dlgparam *dlg, const int nb, const char** sessionslist, const char * savedsession, const char* CurrentFolder) {
|
||||
int i, j=0; char **s ; bool * tabb ;
|
||||
char folder[1024] ;
|
||||
// init
|
||||
s = (char**)malloc( nb*sizeof(char*)) ;
|
||||
tabb = (bool*)malloc(nb*sizeof(bool)) ;
|
||||
for( i=0 ; i<nb ; i++ ) { s[i] = NULL ; tabb[i] = true ; }
|
||||
|
||||
|
||||
// Adding directories
|
||||
if( GetDirectoryBrowseFlag() )
|
||||
for( i=0 ; i<nb ; i++ ) if( tabb[i] ) {
|
||||
@@ -1000,14 +1009,26 @@ void filter_session_portable(union control *ctrl, dlgparam *dlg, const int nb, c
|
||||
}
|
||||
}
|
||||
|
||||
// Managing browsedirectory setting
|
||||
if( !GetDirectoryBrowseFlag() && strcmp(CurrentFolder,"Default") )
|
||||
for( i=0 ; i<nb ; i++ ) if( tabb[i] ) {
|
||||
GetSessionFolderName( sessionslist[i], folder ) ;
|
||||
if( strcmp(CurrentFolder,folder) ) { tabb[i] = false ; }
|
||||
}
|
||||
|
||||
// Adding Default Settings Session only on Main folder
|
||||
for( i=0 ; i<nb ; i++ ) if( tabb[i] ) {
|
||||
if( strstr(sessionslist[i],"Default Settings")==sessionslist[i] ) {
|
||||
if( !strcmp(CurrentFolder,"Default") ) sessionlist_add(s,sessionslist[i],&j,true) ;
|
||||
if( !strcmp(CurrentFolder,"Default") && SessPathIsInitial() ) sessionlist_add(s,sessionslist[i],&j,true) ;
|
||||
tabb[i] = false ;
|
||||
}
|
||||
}
|
||||
|
||||
// Removing session that starts with __
|
||||
for( i=0 ; i<nb ; i++ ) if( tabb[i] ) {
|
||||
if( (sessionslist[i][0]=='_')&&(sessionslist[i][1]=='_') ) { }
|
||||
}
|
||||
|
||||
// Adding other session depending on the filter
|
||||
for( i=0 ; i<nb ; i++ ) if( tabb[i] ) {
|
||||
if( !GetSessionFilterFlag() ) { // On filter disable
|
||||
@@ -1018,7 +1039,12 @@ void filter_session_portable(union control *ctrl, dlgparam *dlg, const int nb, c
|
||||
tabb[i] = false ;
|
||||
}
|
||||
|
||||
for( i=0 ; i<nb ; i++ ) { if( s[i] != NULL ) { dlg_listbox_add(ctrl, dlg, s[i]) ; } }
|
||||
for( i=0 ; i<nb ; i++ ) {
|
||||
if( s[i] != NULL ) {
|
||||
dlg_listbox_add(ctrl, dlg, s[i]) ;
|
||||
if( !strcmp(s[i],selectedsession) ) { dlg_listbox_select(ctrl, dlg, i); }
|
||||
}
|
||||
}
|
||||
// cleaning
|
||||
for( i=0 ; i<nb ; i++ ) { if( s[i] != NULL ) { free(s[i]) ; } }
|
||||
free(tabb);
|
||||
@@ -1037,6 +1063,7 @@ static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
|
||||
if (ctrl == ssd->editbox) {
|
||||
#ifdef MOD_PERSO
|
||||
ctrlSessionEdit = ctrl ;
|
||||
if( selectedsession==NULL ) { selectedsession=(char*)malloc(1);strcpy(selectedsession,""); }
|
||||
#endif
|
||||
dlg_editbox_set(ctrl, dlg, ssd->savedsession);
|
||||
} else if (ctrl == ssd->listbox) {
|
||||
@@ -1044,16 +1071,15 @@ static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
|
||||
dlg_update_start(ctrl, dlg);
|
||||
dlg_listbox_clear(ctrl, dlg);
|
||||
#ifdef MOD_PERSO
|
||||
if( ssd->savedsession!=NULL ) strcpy( ssd->savedsession, dlg_editbox_get( ssd->editbox, dlg ) ) ; // ajout 0.63 pour que le filtre fonctionne
|
||||
|
||||
if( ssd->savedsession!=NULL ) strcpy( ssd->savedsession, dlg_editbox_get( ssd->editbox, dlg ) ) ;
|
||||
ctrlSessionList = ctrl ;
|
||||
if(get_param("INIFILE")==SAVEMODE_DIR) CleanFolderName( CurrentFolder ) ;
|
||||
|
||||
//if(get_param("INIFILE")==SAVEMODE_DIR) CleanFolderName( CurrentFolder ) ;
|
||||
int j;
|
||||
if( get_param("INIFILE")==SAVEMODE_DIR ) {
|
||||
filter_session_portable(ctrl, dlg, ssd->sesslist.nsessions,ssd->sesslist.sessions, ssd->savedsession ) ;
|
||||
filter_session_portable(ctrl, dlg, ssd->sesslist.nsessions,ssd->sesslist.sessions, ssd->savedsession, CurrentFolder ) ;
|
||||
}
|
||||
else
|
||||
for (i = 0; i < ssd->sesslist.nsessions; i++) {
|
||||
for (i = 0, j=0; i < ssd->sesslist.nsessions; i++) {
|
||||
char folder[1024] ;
|
||||
strcpy( folder, "" ) ;
|
||||
if( (get_param("INIFILE")==SAVEMODE_REG) || ((get_param("INIFILE")==SAVEMODE_DIR) && !GetDirectoryBrowseFlag()) ) { // Registry mode
|
||||
@@ -1061,8 +1087,8 @@ static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
|
||||
}
|
||||
if( GetPuttyFlag() ) { // In PuTTY mode => all sessions
|
||||
dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
|
||||
}
|
||||
|
||||
}
|
||||
else if( (ssd->sesslist.sessions[i][0]=='_') && (ssd->sesslist.sessions[i][1]=='_') ) { } // Do nothing if session name starts with __
|
||||
else if( !GetSessionFilterFlag() ) // In registry mode without filter => all sessions
|
||||
dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
|
||||
else { // In registry mode with filter
|
||||
@@ -1075,18 +1101,23 @@ static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
|
||||
|| ( filter_sessionname( "comment:", ssd->sesslist.sessions[i], folder, ssd->savedsession ) )
|
||||
|| ( filter_sessionname( "title:", ssd->sesslist.sessions[i], folder, ssd->savedsession ) )
|
||||
|| ( filter_sessionname( "class:", ssd->sesslist.sessions[i], folder, ssd->savedsession ) )
|
||||
//|| (stristr(host,ssd->savedsession)!=NULL) /* Filtre sur le nom du host */
|
||||
|| (stristr(ssd->sesslist.sessions[i],ssd->savedsession)!=NULL) )
|
||||
|| ( stristr(ssd->sesslist.sessions[i],ssd->savedsession)!=NULL) ) {
|
||||
dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]) ;
|
||||
if( !strcmp(ssd->sesslist.sessions[i],selectedsession) ) { dlg_listbox_select(ctrl, dlg, j); }
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
dlg_update_done(ctrl, dlg);
|
||||
//if( get_param("INIFILE")!=SAVEMODE_DIR ) { dlg_listbox_select(ctrl,dlg,0) ; }
|
||||
#else
|
||||
for (i = 0; i < ssd->sesslist.nsessions; i++)
|
||||
dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
|
||||
#endif
|
||||
dlg_update_done(ctrl, dlg);
|
||||
#endif
|
||||
|
||||
}
|
||||
} else if (event == EVENT_VALCHANGE) {
|
||||
int top, bottom, halfway, i;
|
||||
@@ -1125,27 +1156,26 @@ static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
|
||||
* contains a hostname.
|
||||
*/
|
||||
#ifdef MOD_PERSO
|
||||
if (load_selected_session(ssd, dlg, conf, &mbl) &&
|
||||
(mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
|
||||
if (load_selected_session(ssd, dlg, conf, &mbl) &&
|
||||
(mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
|
||||
if( conf_get_int(conf, CONF_protocol) != PROT_SERIAL ) {
|
||||
char buffer[1024] ;
|
||||
strcpy( buffer, conf_get_str( conf, CONF_password) ) ;
|
||||
MASKPASS( buffer ) ;
|
||||
MASKPASS( GetCryptSaltFlag(), buffer ) ;
|
||||
conf_set_str( conf, CONF_password, buffer ) ;
|
||||
memset( buffer, 0, strlen(buffer) ) ;
|
||||
}
|
||||
}
|
||||
if( GetDblClickFlag()==1 ) {
|
||||
sessionsaver_handler( ssd->startbutton, dlg, data, EVENT_ACTION ) ;
|
||||
sessionsaver_handler( ssd->clearbutton, dlg, data, EVENT_ACTION ) ;
|
||||
sessionsaver_handler( ctrlSessionList, dlg, data, EVENT_REFRESH ) ;
|
||||
}
|
||||
else
|
||||
dlg_end(dlg, 1); /* it's all over, and succeeded */
|
||||
}
|
||||
if( conf_get_int(conf, CONF_protocol) != PROT_SERIAL ) {
|
||||
sessionsaver_handler( ssd->startbutton, dlg, data, EVENT_ACTION ) ;
|
||||
sessionsaver_handler( ssd->clearbutton, dlg, data, EVENT_ACTION ) ;
|
||||
sessionsaver_handler( ctrlSessionList, dlg, data, EVENT_REFRESH ) ;
|
||||
} else
|
||||
dlg_end(dlg, 1); /* it's all over, and succeeded */
|
||||
}
|
||||
if( conf_get_int(conf, CONF_protocol) != PROT_SERIAL ) {
|
||||
char buffer[1024] ;
|
||||
strcpy( buffer, conf_get_str( conf, CONF_password) ) ;
|
||||
MASKPASS( buffer ) ;
|
||||
MASKPASS( GetCryptSaltFlag(), buffer ) ;
|
||||
conf_set_str( conf, CONF_password, buffer ) ;
|
||||
memset( buffer, 0, strlen(buffer) ) ;
|
||||
}
|
||||
@@ -1174,7 +1204,7 @@ static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
|
||||
if( conf_get_int(conf, CONF_protocol) != PROT_SERIAL ) {
|
||||
char buffer[1024] ;
|
||||
strcpy( buffer, conf_get_str( conf, CONF_password) ) ;
|
||||
MASKPASS( buffer ) ;
|
||||
MASKPASS( GetCryptSaltFlag(), buffer ) ;
|
||||
conf_set_str( conf, CONF_password, buffer ) ;
|
||||
memset( buffer, 0, strlen(buffer) ) ;
|
||||
}
|
||||
@@ -1186,7 +1216,7 @@ static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
|
||||
if( conf_get_int(conf, CONF_protocol) != PROT_SERIAL ) {
|
||||
char buffer[1024] ;
|
||||
strcpy( buffer, conf_get_str( conf, CONF_password) ) ;
|
||||
MASKPASS( buffer ) ;
|
||||
MASKPASS( GetCryptSaltFlag(), buffer ) ;
|
||||
conf_set_str( conf, CONF_password, buffer ) ;
|
||||
memset( buffer, 0, strlen(buffer) ) ;
|
||||
}
|
||||
@@ -1248,7 +1278,7 @@ static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
|
||||
if( conf_get_int(conf, CONF_protocol) != PROT_SERIAL ) {
|
||||
char buffer[1024] ;
|
||||
strcpy( buffer, conf_get_str( conf, CONF_password) ) ;
|
||||
MASKPASS( buffer ) ;
|
||||
MASKPASS( GetCryptSaltFlag(), buffer ) ;
|
||||
conf_set_str( conf, CONF_password, buffer ) ;
|
||||
memset( buffer, 0, strlen(buffer) ) ;
|
||||
}
|
||||
@@ -1292,7 +1322,7 @@ static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
|
||||
dlg_end(dlg, 0);
|
||||
}
|
||||
#ifdef MOD_PERSO
|
||||
#if (defined MOD_PERSO) && (!defined FDJ) && (defined MOD_STARTBUTTON)
|
||||
#if (defined MOD_PERSO) && (!defined FLJ) && (defined MOD_STARTBUTTON)
|
||||
else if (ctrl == ssd->startbutton) {
|
||||
char sessionname[1024];
|
||||
if( conf_launchable(conf) ) {
|
||||
@@ -1849,7 +1879,7 @@ static void environ_handler(union control *ctrl, dlgparam *dlg,
|
||||
return;
|
||||
}
|
||||
conf_set_str_str(conf, CONF_environmt, key, val);
|
||||
str = dupcat(key, "\t", val, NULL);
|
||||
str = dupcat(key, "\t", val);
|
||||
dlg_editbox_set(ed->varbox, dlg, "");
|
||||
dlg_editbox_set(ed->valbox, dlg, "");
|
||||
sfree(str);
|
||||
@@ -1980,7 +2010,7 @@ static void portfwd_handler(union control *ctrl, dlgparam *dlg,
|
||||
val = dupstr("D"); /* special case */
|
||||
}
|
||||
|
||||
key = dupcat(family, type, src, NULL);
|
||||
key = dupcat(family, type, src);
|
||||
sfree(src);
|
||||
|
||||
if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
|
||||
@@ -2147,7 +2177,7 @@ static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg,
|
||||
if (!strcmp(sval, options[i].name))
|
||||
break; /* needs escaping */
|
||||
if (i < lenof(options) || sval[0] == '=') {
|
||||
char *escaped = dupcat("=", sval, (const char *)NULL);
|
||||
char *escaped = dupcat("=", sval);
|
||||
dlg_editbox_set(ctrl, dlg, escaped);
|
||||
sfree(escaped);
|
||||
} else {
|
||||
@@ -2174,7 +2204,7 @@ static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg,
|
||||
#endif
|
||||
) {
|
||||
#ifdef NAMED_CLIPBOARDS
|
||||
const char *sval = dlg_editbox_get(ctrl, dlg);
|
||||
char *sval = dlg_editbox_get(ctrl, dlg);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < lenof(options); i++)
|
||||
@@ -2189,6 +2219,7 @@ static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg,
|
||||
sval++;
|
||||
conf_set_str(conf, strsetting, sval);
|
||||
}
|
||||
sfree(sval);
|
||||
#else
|
||||
int index = dlg_listbox_index(ctrl, dlg);
|
||||
if (index >= 0) {
|
||||
@@ -2250,7 +2281,7 @@ void setup_config_box(struct controlbox *b, bool midsession,
|
||||
if( GetConfigBoxHeight() > 7 ) ssd->okbutton->generic.column = 0 ; else
|
||||
#endif
|
||||
ssd->okbutton->generic.column = 3;
|
||||
#if (defined MOD_PERSO) && (!defined FDJ) && (defined MOD_STARTBUTTON)
|
||||
#if (defined MOD_PERSO) && (!defined FLJ) && (defined MOD_STARTBUTTON)
|
||||
if( (!midsession)&&(!GetPuttyFlag()) ) {
|
||||
ssd->startbutton = ctrl_pushbutton(s, "Start", NO_SHORTCUT, HELPCTX(no_help), sessionsaver_handler, P(ssd));
|
||||
if( GetConfigBoxHeight() > 7 ) ssd->startbutton->generic.column = 0 ; else ssd->startbutton->generic.column = 3;
|
||||
@@ -2340,6 +2371,7 @@ void setup_config_box(struct controlbox *b, bool midsession,
|
||||
ssd->editbox = ctrl_editbox(s, "Saved Sessions/New Folder", 'e', 100,
|
||||
HELPCTX(session_saved),
|
||||
sessionsaver_handler, P(ssd), P(NULL));
|
||||
|
||||
ssd->editbox->generic.column = 0;
|
||||
if( !midsession && ( (get_param("INIFILE")!=2)||(!GetDirectoryBrowseFlag()) ) ) {
|
||||
ssd->clearbutton = ctrl_pushbutton(s, "Clear", NO_SHORTCUT,
|
||||
@@ -2366,6 +2398,7 @@ void setup_config_box(struct controlbox *b, bool midsession,
|
||||
ssd->listbox->generic.column = 0;
|
||||
#ifdef MOD_PERSO
|
||||
ssd->listbox->listbox.height = GetConfigBoxHeight() ;
|
||||
ssd->listbox->listbox.hscroll = true ; /* Does nothing */
|
||||
#else
|
||||
ssd->listbox->listbox.height = 7;
|
||||
#endif
|
||||
@@ -2441,7 +2474,7 @@ void setup_config_box(struct controlbox *b, bool midsession,
|
||||
ctrl_droplist(s, "Folder", NO_SHORTCUT, 80,
|
||||
HELPCTX(no_help),
|
||||
folder_handler, I(CONF_folder)) ;
|
||||
}
|
||||
}
|
||||
#else
|
||||
ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
|
||||
HELPCTX(session_saved),
|
||||
@@ -2814,6 +2847,17 @@ s = ctrl_getset(b, "Terminal/Bell", "overload",
|
||||
ctrl_editbox(s, "Lines of scrollback", 's', 50,
|
||||
HELPCTX(window_scrollback),
|
||||
conf_editbox_handler, I(CONF_savelines), I(-1));
|
||||
#if MOD_PERSO
|
||||
/*
|
||||
* PuttyFeatures: Scroll Lines
|
||||
*/
|
||||
ctrl_text(s, "Set the number of lines to scroll back per wheel turn."
|
||||
" Use -1 for half screen, -2 full screen",
|
||||
HELPCTX(colours_config));
|
||||
ctrl_editbox(s, "Lines scrolled per turn", 'l', 50,
|
||||
HELPCTX(window_scrollback),
|
||||
conf_editbox_handler, I(CONF_scrolllines), I(-1));
|
||||
#endif
|
||||
ctrl_checkbox(s, "Display scrollbar", 'd',
|
||||
HELPCTX(window_scrollback),
|
||||
conf_checkbox_handler, I(CONF_scrollbar));
|
||||
@@ -2900,7 +2944,7 @@ s = ctrl_getset(b, "Terminal/Bell", "overload",
|
||||
|
||||
}
|
||||
#endif
|
||||
#if (defined MOD_BACKGROUNDIMAGE) && (!defined FDJ)
|
||||
#if (defined MOD_BACKGROUNDIMAGE) && (!defined FLJ)
|
||||
if( !GetPuttyFlag() && GetBackgroundImageFlag() ) {
|
||||
/*
|
||||
* The Window/Background panel.
|
||||
@@ -3008,7 +3052,8 @@ if( !GetPuttyFlag() ) {
|
||||
ctrl_text(s, " %%P: protocol name", HELPCTX(appearance_title));
|
||||
ctrl_text(s, " %%s: session name", HELPCTX(appearance_title));
|
||||
ctrl_text(s, " %%u: username", HELPCTX(appearance_title));
|
||||
ctrl_text(s, " %%w: forwarded ports list", HELPCTX(appearance_title));
|
||||
ctrl_text(s, " %%l: forwarded local ports list", HELPCTX(appearance_title));
|
||||
ctrl_text(s, " %%d: forwarded dynamic ports list", HELPCTX(appearance_title));
|
||||
}
|
||||
#endif
|
||||
ctrl_checkbox(s, "Separate window and icon titles", 'i',
|
||||
@@ -3317,7 +3362,8 @@ if( !GetPuttyFlag() ) {
|
||||
c = ctrl_editbox(s, "Auto-login password", NO_SHORTCUT, 50,
|
||||
HELPCTX(no_help),
|
||||
conf_editbox_handler, I(CONF_password), I(1) ) ;
|
||||
c->editbox.password = 1;
|
||||
//if( !debug_flag )
|
||||
c->editbox.password = 1;
|
||||
#endif
|
||||
ctrlAutoCommandContentEdit = ctrl_editbox(s, "Command", NO_SHORTCUT, 74,
|
||||
HELPCTX(no_help),
|
||||
@@ -3637,6 +3683,10 @@ if( !GetPuttyFlag() ) {
|
||||
HELPCTX(ssh_hklist),
|
||||
hklist_handler, P(NULL));
|
||||
c->listbox.height = 5;
|
||||
|
||||
ctrl_checkbox(s, "Prefer algorithms for which a host key is known",
|
||||
'p', HELPCTX(ssh_hk_known), conf_checkbox_handler,
|
||||
I(CONF_ssh_prefer_known_hostkeys));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4022,7 +4072,7 @@ if( !GetPuttyFlag() ) {
|
||||
"PSCP and WinSCP integration");
|
||||
|
||||
s = ctrl_getset(b, "Connection/SSH/PSCP and WinSCP", "winSCPproto", "General protocol setting") ;
|
||||
ctrl_radiobuttons(s, "Prefered protocol:", NO_SHORTCUT, 7,
|
||||
ctrl_radiobuttons(s, "Prefered protocol:", NO_SHORTCUT, 4,
|
||||
HELPCTX(no_help),
|
||||
conf_radiobutton_handler,
|
||||
I(CONF_winscpprot),
|
||||
@@ -4062,6 +4112,10 @@ if( !GetPuttyFlag() ) {
|
||||
HELPCTX(no_help),
|
||||
conf_editbox_handler, I(CONF_winscprawsettings),
|
||||
I(1));
|
||||
ctrl_editbox(s, "Shell (scp mode only)", NO_SHORTCUT, 100,
|
||||
HELPCTX(no_help),
|
||||
conf_editbox_handler, I(CONF_pscpshell),
|
||||
I(1));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4092,6 +4146,8 @@ if( !GetPuttyFlag() ) {
|
||||
50,
|
||||
HELPCTX(no_help),
|
||||
conf_editbox_handler, I(CONF_rzoptions), I(1));
|
||||
ctrl_text(s, "Ctrl+X to quit rz before completing",
|
||||
HELPCTX(no_help));
|
||||
|
||||
ctrl_settitle(b, "Connection/ZModem/sz",
|
||||
"sz path and options");
|
||||
@@ -22,11 +22,38 @@
|
||||
#define PRIdMAX "I64d"
|
||||
#define PRIXMAX "I64X"
|
||||
#define SCNu64 "I64u"
|
||||
#define SIZEx "Ix"
|
||||
#define SIZEu "Iu"
|
||||
uintmax_t strtoumax(const char *nptr, char **endptr, int base);
|
||||
#else
|
||||
#include <inttypes.h>
|
||||
/* Because we still support older MSVC libraries which don't recognise the
|
||||
* standard C "z" modifier for size_t-sized integers, we must use an
|
||||
* inttypes.h-style macro for those */
|
||||
#define SIZEx "zx"
|
||||
#define SIZEu "zu"
|
||||
#endif
|
||||
|
||||
#if defined __GNUC__ || defined __clang__
|
||||
/*
|
||||
* On MinGW, the correct compiler format checking for vsnprintf() etc
|
||||
* can depend on compile-time flags; these control whether you get
|
||||
* ISO C or Microsoft's non-standard format strings.
|
||||
* We sometimes use __attribute__ ((format)) for our own printf-like
|
||||
* functions, which are ultimately interpreted by the toolchain-chosen
|
||||
* printf, so we need to take that into account to get correct warnings.
|
||||
*/
|
||||
#ifdef __MINGW_PRINTF_FORMAT
|
||||
#define PRINTF_LIKE(fmt_index, ellipsis_index) \
|
||||
__attribute__ ((format (__MINGW_PRINTF_FORMAT, fmt_index, ellipsis_index)))
|
||||
#else
|
||||
#define PRINTF_LIKE(fmt_index, ellipsis_index) \
|
||||
__attribute__ ((format (printf, fmt_index, ellipsis_index)))
|
||||
#endif
|
||||
#else /* __GNUC__ */
|
||||
#define PRINTF_LIKE(fmt_index, ellipsis_index)
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
typedef struct conf_tag Conf;
|
||||
typedef struct terminal_tag Terminal;
|
||||
typedef struct term_utf8_decode term_utf8_decode;
|
||||
@@ -352,7 +352,6 @@ union control *ctrl_listbox(struct controlset *s, const char *label,
|
||||
c->listbox.percentwidth = 100;
|
||||
c->listbox.ncols = 0;
|
||||
c->listbox.percentages = NULL;
|
||||
c->listbox.hscroll = true;
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -1,223 +1,228 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define PUTTY_DO_GLOBALS
|
||||
#include "putty.h"
|
||||
#include "terminal.h"
|
||||
|
||||
/* For Unix in particular, but harmless if this main() is reused elsewhere */
|
||||
const bool buildinfo_gtk_relevant = false;
|
||||
|
||||
static const TermWinVtable fuzz_termwin_vt;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char blk[512];
|
||||
size_t len;
|
||||
Terminal *term;
|
||||
Conf *conf;
|
||||
struct unicode_data ucsdata;
|
||||
TermWin termwin;
|
||||
|
||||
termwin.vt = &fuzz_termwin_vt;
|
||||
|
||||
conf = conf_new();
|
||||
do_defaults(NULL, conf);
|
||||
init_ucs(&ucsdata, conf_get_str(conf, CONF_line_codepage),
|
||||
conf_get_bool(conf, CONF_utf8_override),
|
||||
CS_NONE, conf_get_int(conf, CONF_vtmode));
|
||||
|
||||
term = term_init(conf, &ucsdata, &termwin);
|
||||
term_size(term, 24, 80, 10000);
|
||||
term->ldisc = NULL;
|
||||
/* Tell american fuzzy lop that this is a good place to fork. */
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
__AFL_INIT();
|
||||
#endif
|
||||
while (!feof(stdin)) {
|
||||
len = fread(blk, 1, sizeof(blk), stdin);
|
||||
term_data(term, false, blk, len);
|
||||
}
|
||||
term_update(term);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* functions required by terminal.c */
|
||||
static bool fuzz_setup_draw_ctx(TermWin *tw) { return true; }
|
||||
static void fuzz_draw_text(
|
||||
TermWin *tw, int x, int y, wchar_t *text, int len,
|
||||
unsigned long attr, int lattr, truecolour tc)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("TEXT[attr=%08lx,lattr=%02x]@(%d,%d):", attr, lattr, x, y);
|
||||
for (i = 0; i < len; i++) {
|
||||
printf(" %x", (unsigned)text[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
static void fuzz_draw_cursor(
|
||||
TermWin *tw, int x, int y, wchar_t *text, int len,
|
||||
unsigned long attr, int lattr, truecolour tc)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("CURS[attr=%08lx,lattr=%02x]@(%d,%d):", attr, lattr, x, y);
|
||||
for (i = 0; i < len; i++) {
|
||||
printf(" %x", (unsigned)text[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
static void fuzz_draw_trust_sigil(TermWin *tw, int x, int y)
|
||||
{
|
||||
printf("TRUST@(%d,%d)\n", x, y);
|
||||
}
|
||||
static int fuzz_char_width(TermWin *tw, int uc) { return 1; }
|
||||
static void fuzz_free_draw_ctx(TermWin *tw) {}
|
||||
static void fuzz_set_cursor_pos(TermWin *tw, int x, int y) {}
|
||||
static void fuzz_set_raw_mouse_mode(TermWin *tw, bool enable) {}
|
||||
static void fuzz_set_scrollbar(TermWin *tw, int total, int start, int page) {}
|
||||
static void fuzz_bell(TermWin *tw, int mode) {}
|
||||
static void fuzz_clip_write(
|
||||
TermWin *tw, int clipboard, wchar_t *text, int *attrs,
|
||||
truecolour *colours, int len, bool must_deselect) {}
|
||||
static void fuzz_clip_request_paste(TermWin *tw, int clipboard) {}
|
||||
static void fuzz_refresh(TermWin *tw) {}
|
||||
static void fuzz_request_resize(TermWin *tw, int w, int h) {}
|
||||
static void fuzz_set_title(TermWin *tw, const char *title) {}
|
||||
static void fuzz_set_icon_title(TermWin *tw, const char *icontitle) {}
|
||||
static void fuzz_set_minimised(TermWin *tw, bool minimised) {}
|
||||
static bool fuzz_is_minimised(TermWin *tw) { return false; }
|
||||
static void fuzz_set_maximised(TermWin *tw, bool maximised) {}
|
||||
static void fuzz_move(TermWin *tw, int x, int y) {}
|
||||
static void fuzz_set_zorder(TermWin *tw, bool top) {}
|
||||
static bool fuzz_palette_get(TermWin *tw, int n, int *r, int *g, int *b)
|
||||
{ return false; }
|
||||
static void fuzz_palette_set(TermWin *tw, int n, int r, int g, int b) {}
|
||||
static void fuzz_palette_reset(TermWin *tw) {}
|
||||
static void fuzz_get_pos(TermWin *tw, int *x, int *y) { *x = *y = 0; }
|
||||
static void fuzz_get_pixels(TermWin *tw, int *x, int *y) { *x = *y = 0; }
|
||||
static const char *fuzz_get_title(TermWin *tw, bool icon) { return "moo"; }
|
||||
static bool fuzz_is_utf8(TermWin *tw) { return true; }
|
||||
|
||||
static const TermWinVtable fuzz_termwin_vt = {
|
||||
fuzz_setup_draw_ctx,
|
||||
fuzz_draw_text,
|
||||
fuzz_draw_cursor,
|
||||
fuzz_draw_trust_sigil,
|
||||
fuzz_char_width,
|
||||
fuzz_free_draw_ctx,
|
||||
fuzz_set_cursor_pos,
|
||||
fuzz_set_raw_mouse_mode,
|
||||
fuzz_set_scrollbar,
|
||||
fuzz_bell,
|
||||
fuzz_clip_write,
|
||||
fuzz_clip_request_paste,
|
||||
fuzz_refresh,
|
||||
fuzz_request_resize,
|
||||
fuzz_set_title,
|
||||
fuzz_set_icon_title,
|
||||
fuzz_set_minimised,
|
||||
fuzz_is_minimised,
|
||||
fuzz_set_maximised,
|
||||
fuzz_move,
|
||||
fuzz_set_zorder,
|
||||
fuzz_palette_get,
|
||||
fuzz_palette_set,
|
||||
fuzz_palette_reset,
|
||||
fuzz_get_pos,
|
||||
fuzz_get_pixels,
|
||||
fuzz_get_title,
|
||||
fuzz_is_utf8,
|
||||
};
|
||||
|
||||
void ldisc_send(Ldisc *ldisc, const void *buf, int len, bool interactive) {}
|
||||
void ldisc_echoedit_update(Ldisc *ldisc) {}
|
||||
void modalfatalbox(const char *fmt, ...) { exit(0); }
|
||||
void nonfatal(const char *fmt, ...) { }
|
||||
|
||||
/* needed by timing.c */
|
||||
void timer_change_notify(unsigned long next) { }
|
||||
|
||||
/* needed by config.c and sercfg.c */
|
||||
|
||||
void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton) { }
|
||||
int dlg_radiobutton_get(union control *ctrl, void *dlg) { return 0; }
|
||||
void dlg_checkbox_set(union control *ctrl, void *dlg, int checked) { }
|
||||
int dlg_checkbox_get(union control *ctrl, void *dlg) { return 0; }
|
||||
void dlg_editbox_set(union control *ctrl, void *dlg, char const *text) { }
|
||||
char *dlg_editbox_get(union control *ctrl, void *dlg) { return dupstr("moo"); }
|
||||
void dlg_listbox_clear(union control *ctrl, void *dlg) { }
|
||||
void dlg_listbox_del(union control *ctrl, void *dlg, int index) { }
|
||||
void dlg_listbox_add(union control *ctrl, void *dlg, char const *text) { }
|
||||
void dlg_listbox_addwithid(union control *ctrl, void *dlg,
|
||||
char const *text, int id) { }
|
||||
int dlg_listbox_getid(union control *ctrl, void *dlg, int index) { return 0; }
|
||||
int dlg_listbox_index(union control *ctrl, void *dlg) { return -1; }
|
||||
int dlg_listbox_issel(union control *ctrl, void *dlg, int index) { return 0; }
|
||||
void dlg_listbox_select(union control *ctrl, void *dlg, int index) { }
|
||||
void dlg_text_set(union control *ctrl, void *dlg, char const *text) { }
|
||||
void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn) { }
|
||||
Filename *dlg_filesel_get(union control *ctrl, void *dlg) { return NULL; }
|
||||
void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fn) { }
|
||||
FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg) { return NULL; }
|
||||
void dlg_update_start(union control *ctrl, void *dlg) { }
|
||||
void dlg_update_done(union control *ctrl, void *dlg) { }
|
||||
void dlg_set_focus(union control *ctrl, void *dlg) { }
|
||||
void dlg_label_change(union control *ctrl, void *dlg, char const *text) { }
|
||||
union control *dlg_last_focused(union control *ctrl, void *dlg) { return NULL; }
|
||||
void dlg_beep(void *dlg) { }
|
||||
void dlg_error_msg(void *dlg, const char *msg) { }
|
||||
void dlg_end(void *dlg, int value) { }
|
||||
void dlg_coloursel_start(union control *ctrl, void *dlg,
|
||||
int r, int g, int b) { }
|
||||
bool dlg_coloursel_results(union control *ctrl, void *dlg,
|
||||
int *r, int *g, int *b) { return false; }
|
||||
void dlg_refresh(union control *ctrl, void *dlg) { }
|
||||
bool dlg_is_visible(union control *ctrl, dlgparam *dp) { return false; }
|
||||
|
||||
const char *const appname = "FuZZterm";
|
||||
const int ngsslibs = 0;
|
||||
const char *const gsslibnames[0] = { };
|
||||
const struct keyvalwhere gsslibkeywords[0] = { };
|
||||
|
||||
/*
|
||||
* Default settings that are specific to Unix plink.
|
||||
*/
|
||||
char *platform_default_s(const char *name)
|
||||
{
|
||||
if (!strcmp(name, "TermType"))
|
||||
return dupstr(getenv("TERM"));
|
||||
if (!strcmp(name, "SerialLine"))
|
||||
return dupstr("/dev/ttyS0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool platform_default_b(const char *name, bool def)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
|
||||
int platform_default_i(const char *name, int def)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
|
||||
FontSpec *platform_default_fontspec(const char *name)
|
||||
{
|
||||
return fontspec_new("");
|
||||
}
|
||||
|
||||
Filename *platform_default_filename(const char *name)
|
||||
{
|
||||
if (!strcmp(name, "LogFileName"))
|
||||
return filename_from_str("putty.log");
|
||||
else
|
||||
return filename_from_str("");
|
||||
}
|
||||
|
||||
char *x_get_default(const char *key)
|
||||
{
|
||||
return NULL; /* this is a stub */
|
||||
}
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define PUTTY_DO_GLOBALS
|
||||
#include "putty.h"
|
||||
#include "dialog.h"
|
||||
#include "terminal.h"
|
||||
|
||||
/* For Unix in particular, but harmless if this main() is reused elsewhere */
|
||||
const bool buildinfo_gtk_relevant = false;
|
||||
|
||||
static const TermWinVtable fuzz_termwin_vt;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char blk[512];
|
||||
size_t len;
|
||||
Terminal *term;
|
||||
Conf *conf;
|
||||
struct unicode_data ucsdata;
|
||||
TermWin termwin;
|
||||
|
||||
termwin.vt = &fuzz_termwin_vt;
|
||||
|
||||
conf = conf_new();
|
||||
do_defaults(NULL, conf);
|
||||
init_ucs(&ucsdata, conf_get_str(conf, CONF_line_codepage),
|
||||
conf_get_bool(conf, CONF_utf8_override),
|
||||
CS_NONE, conf_get_int(conf, CONF_vtmode));
|
||||
|
||||
term = term_init(conf, &ucsdata, &termwin);
|
||||
term_size(term, 24, 80, 10000);
|
||||
term->ldisc = NULL;
|
||||
/* Tell american fuzzy lop that this is a good place to fork. */
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
__AFL_INIT();
|
||||
#endif
|
||||
while (!feof(stdin)) {
|
||||
len = fread(blk, 1, sizeof(blk), stdin);
|
||||
term_data(term, false, blk, len);
|
||||
}
|
||||
term_update(term);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* functions required by terminal.c */
|
||||
static bool fuzz_setup_draw_ctx(TermWin *tw) { return true; }
|
||||
static void fuzz_draw_text(
|
||||
TermWin *tw, int x, int y, wchar_t *text, int len,
|
||||
unsigned long attr, int lattr, truecolour tc)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("TEXT[attr=%08lx,lattr=%02x]@(%d,%d):", attr, lattr, x, y);
|
||||
for (i = 0; i < len; i++) {
|
||||
printf(" %x", (unsigned)text[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
static void fuzz_draw_cursor(
|
||||
TermWin *tw, int x, int y, wchar_t *text, int len,
|
||||
unsigned long attr, int lattr, truecolour tc)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("CURS[attr=%08lx,lattr=%02x]@(%d,%d):", attr, lattr, x, y);
|
||||
for (i = 0; i < len; i++) {
|
||||
printf(" %x", (unsigned)text[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
static void fuzz_draw_trust_sigil(TermWin *tw, int x, int y)
|
||||
{
|
||||
printf("TRUST@(%d,%d)\n", x, y);
|
||||
}
|
||||
static int fuzz_char_width(TermWin *tw, int uc) { return 1; }
|
||||
static void fuzz_free_draw_ctx(TermWin *tw) {}
|
||||
static void fuzz_set_cursor_pos(TermWin *tw, int x, int y) {}
|
||||
static void fuzz_set_raw_mouse_mode(TermWin *tw, bool enable) {}
|
||||
static void fuzz_set_scrollbar(TermWin *tw, int total, int start, int page) {}
|
||||
static void fuzz_bell(TermWin *tw, int mode) {}
|
||||
static void fuzz_clip_write(
|
||||
TermWin *tw, int clipboard, wchar_t *text, int *attrs,
|
||||
truecolour *colours, int len, bool must_deselect) {}
|
||||
static void fuzz_clip_request_paste(TermWin *tw, int clipboard) {}
|
||||
static void fuzz_refresh(TermWin *tw) {}
|
||||
static void fuzz_request_resize(TermWin *tw, int w, int h) {}
|
||||
static void fuzz_set_title(TermWin *tw, const char *title) {}
|
||||
static void fuzz_set_icon_title(TermWin *tw, const char *icontitle) {}
|
||||
static void fuzz_set_minimised(TermWin *tw, bool minimised) {}
|
||||
static bool fuzz_is_minimised(TermWin *tw) { return false; }
|
||||
static void fuzz_set_maximised(TermWin *tw, bool maximised) {}
|
||||
static void fuzz_move(TermWin *tw, int x, int y) {}
|
||||
static void fuzz_set_zorder(TermWin *tw, bool top) {}
|
||||
static bool fuzz_palette_get(TermWin *tw, int n, int *r, int *g, int *b)
|
||||
{ return false; }
|
||||
static void fuzz_palette_set(TermWin *tw, int n, int r, int g, int b) {}
|
||||
static void fuzz_palette_reset(TermWin *tw) {}
|
||||
static void fuzz_get_pos(TermWin *tw, int *x, int *y) { *x = *y = 0; }
|
||||
static void fuzz_get_pixels(TermWin *tw, int *x, int *y) { *x = *y = 0; }
|
||||
static const char *fuzz_get_title(TermWin *tw, bool icon) { return "moo"; }
|
||||
static bool fuzz_is_utf8(TermWin *tw) { return true; }
|
||||
|
||||
static const TermWinVtable fuzz_termwin_vt = {
|
||||
fuzz_setup_draw_ctx,
|
||||
fuzz_draw_text,
|
||||
fuzz_draw_cursor,
|
||||
fuzz_draw_trust_sigil,
|
||||
fuzz_char_width,
|
||||
fuzz_free_draw_ctx,
|
||||
fuzz_set_cursor_pos,
|
||||
fuzz_set_raw_mouse_mode,
|
||||
fuzz_set_scrollbar,
|
||||
fuzz_bell,
|
||||
fuzz_clip_write,
|
||||
fuzz_clip_request_paste,
|
||||
fuzz_refresh,
|
||||
fuzz_request_resize,
|
||||
fuzz_set_title,
|
||||
fuzz_set_icon_title,
|
||||
fuzz_set_minimised,
|
||||
fuzz_is_minimised,
|
||||
fuzz_set_maximised,
|
||||
fuzz_move,
|
||||
fuzz_set_zorder,
|
||||
fuzz_palette_get,
|
||||
fuzz_palette_set,
|
||||
fuzz_palette_reset,
|
||||
fuzz_get_pos,
|
||||
fuzz_get_pixels,
|
||||
fuzz_get_title,
|
||||
fuzz_is_utf8,
|
||||
};
|
||||
|
||||
void ldisc_send(Ldisc *ldisc, const void *buf, int len, bool interactive) {}
|
||||
void ldisc_echoedit_update(Ldisc *ldisc) {}
|
||||
void modalfatalbox(const char *fmt, ...) { exit(0); }
|
||||
void nonfatal(const char *fmt, ...) { }
|
||||
|
||||
/* needed by timing.c */
|
||||
void timer_change_notify(unsigned long next) { }
|
||||
|
||||
/* needed by config.c and sercfg.c */
|
||||
|
||||
void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int whichbutton) { }
|
||||
int dlg_radiobutton_get(union control *ctrl, dlgparam *dp) { return 0; }
|
||||
void dlg_checkbox_set(union control *ctrl, dlgparam *dp, bool checked) { }
|
||||
bool dlg_checkbox_get(union control *ctrl, dlgparam *dp) { return false; }
|
||||
void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text) { }
|
||||
char *dlg_editbox_get(union control *ctrl, dlgparam *dp)
|
||||
{ return dupstr("moo"); }
|
||||
void dlg_listbox_clear(union control *ctrl, dlgparam *dp) { }
|
||||
void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index) { }
|
||||
void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text) { }
|
||||
void dlg_listbox_addwithid(union control *ctrl, dlgparam *dp,
|
||||
char const *text, int id) { }
|
||||
int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index)
|
||||
{ return 0; }
|
||||
int dlg_listbox_index(union control *ctrl, dlgparam *dp) { return -1; }
|
||||
bool dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index)
|
||||
{ return false; }
|
||||
void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index) { }
|
||||
void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text) { }
|
||||
void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn) { }
|
||||
Filename *dlg_filesel_get(union control *ctrl, dlgparam *dp) { return NULL; }
|
||||
void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fn) { }
|
||||
FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp) { return NULL; }
|
||||
void dlg_update_start(union control *ctrl, dlgparam *dp) { }
|
||||
void dlg_update_done(union control *ctrl, dlgparam *dp) { }
|
||||
void dlg_set_focus(union control *ctrl, dlgparam *dp) { }
|
||||
void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text) { }
|
||||
union control *dlg_last_focused(union control *ctrl, dlgparam *dp)
|
||||
{ return NULL; }
|
||||
void dlg_beep(dlgparam *dp) { }
|
||||
void dlg_error_msg(dlgparam *dp, const char *msg) { }
|
||||
void dlg_end(dlgparam *dp, int value) { }
|
||||
void dlg_coloursel_start(union control *ctrl, dlgparam *dp,
|
||||
int r, int g, int b) { }
|
||||
bool dlg_coloursel_results(union control *ctrl, dlgparam *dp,
|
||||
int *r, int *g, int *b) { return false; }
|
||||
void dlg_refresh(union control *ctrl, dlgparam *dp) { }
|
||||
bool dlg_is_visible(union control *ctrl, dlgparam *dp) { return false; }
|
||||
|
||||
const char *const appname = "FuZZterm";
|
||||
const int ngsslibs = 0;
|
||||
const char *const gsslibnames[0] = { };
|
||||
const struct keyvalwhere gsslibkeywords[0] = { };
|
||||
|
||||
/*
|
||||
* Default settings that are specific to Unix plink.
|
||||
*/
|
||||
char *platform_default_s(const char *name)
|
||||
{
|
||||
if (!strcmp(name, "TermType"))
|
||||
return dupstr(getenv("TERM"));
|
||||
if (!strcmp(name, "SerialLine"))
|
||||
return dupstr("/dev/ttyS0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool platform_default_b(const char *name, bool def)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
|
||||
int platform_default_i(const char *name, int def)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
|
||||
FontSpec *platform_default_fontspec(const char *name)
|
||||
{
|
||||
return fontspec_new("");
|
||||
}
|
||||
|
||||
Filename *platform_default_filename(const char *name)
|
||||
{
|
||||
if (!strcmp(name, "LogFileName"))
|
||||
return filename_from_str("putty.log");
|
||||
else
|
||||
return filename_from_str("");
|
||||
}
|
||||
|
||||
char *x_get_default(const char *key)
|
||||
{
|
||||
return NULL; /* this is a stub */
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -157,9 +157,6 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)
|
||||
int keyflag = 0;
|
||||
|
||||
assert(ldisc->term);
|
||||
#ifndef MOD_PERSO
|
||||
assert(len);
|
||||
#endif
|
||||
|
||||
/* rutty: */
|
||||
#ifdef MOD_RUTTY
|
||||
@@ -191,7 +191,7 @@ static void logwrite(LogContext *ctx, ptrlen data)
|
||||
* Convenience wrapper on logwrite() which printf-formats the
|
||||
* string.
|
||||
*/
|
||||
static void logprintf(LogContext *ctx, const char *fmt, ...)
|
||||
static PRINTF_LIKE(2, 3) void logprintf(LogContext *ctx, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *data;
|
||||
@@ -481,7 +481,7 @@ void log_packet(LogContext *ctx, int direction, int type,
|
||||
/* If we're about to stop omitting, it's time to say how
|
||||
* much we omitted. */
|
||||
if ((blktype != PKTLOG_OMIT) && omitted) {
|
||||
logprintf(ctx, " (%d byte%s omitted)\r\n",
|
||||
logprintf(ctx, " (%"SIZEu" byte%s omitted)\r\n",
|
||||
omitted, (omitted==1?"":"s"));
|
||||
omitted = 0;
|
||||
}
|
||||
@@ -489,7 +489,8 @@ void log_packet(LogContext *ctx, int direction, int type,
|
||||
/* (Re-)initialise dumpdata as necessary
|
||||
* (start of row, or if we've just stopped omitting) */
|
||||
if (!output_pos && !omitted)
|
||||
sprintf(dumpdata, " %08zx%*s\r\n", p-(p%16), 1+3*16+2+16, "");
|
||||
sprintf(dumpdata, " %08"SIZEx"%*s\r\n",
|
||||
p-(p%16), 1+3*16+2+16, "");
|
||||
|
||||
/* Deal with the current byte. */
|
||||
if (blktype == PKTLOG_OMIT) {
|
||||
@@ -524,7 +525,7 @@ void log_packet(LogContext *ctx, int direction, int type,
|
||||
|
||||
/* Tidy up */
|
||||
if (omitted)
|
||||
logprintf(ctx, " (%d byte%s omitted)\r\n",
|
||||
logprintf(ctx, " (%"SIZEu" byte%s omitted)\r\n",
|
||||
omitted, (omitted==1?"":"s"));
|
||||
logflush(ctx);
|
||||
}
|
||||
@@ -1,260 +1,271 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "marshal.h"
|
||||
#include "misc.h"
|
||||
|
||||
void BinarySink_put_data(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
bs->write(bs, data, len);
|
||||
}
|
||||
|
||||
void BinarySink_put_datapl(BinarySink *bs, ptrlen pl)
|
||||
{
|
||||
BinarySink_put_data(bs, pl.ptr, pl.len);
|
||||
}
|
||||
|
||||
void BinarySink_put_padding(BinarySink *bs, size_t len, unsigned char padbyte)
|
||||
{
|
||||
char buf[16];
|
||||
memset(buf, padbyte, sizeof(buf));
|
||||
while (len > 0) {
|
||||
size_t thislen = len < sizeof(buf) ? len : sizeof(buf);
|
||||
bs->write(bs, buf, thislen);
|
||||
len -= thislen;
|
||||
}
|
||||
}
|
||||
|
||||
void BinarySink_put_byte(BinarySink *bs, unsigned char val)
|
||||
{
|
||||
bs->write(bs, &val, 1);
|
||||
}
|
||||
|
||||
void BinarySink_put_bool(BinarySink *bs, bool val)
|
||||
{
|
||||
unsigned char cval = val ? 1 : 0;
|
||||
bs->write(bs, &cval, 1);
|
||||
}
|
||||
|
||||
void BinarySink_put_uint16(BinarySink *bs, unsigned long val)
|
||||
{
|
||||
unsigned char data[2];
|
||||
PUT_16BIT_MSB_FIRST(data, val);
|
||||
bs->write(bs, data, sizeof(data));
|
||||
}
|
||||
|
||||
void BinarySink_put_uint32(BinarySink *bs, unsigned long val)
|
||||
{
|
||||
unsigned char data[4];
|
||||
PUT_32BIT_MSB_FIRST(data, val);
|
||||
bs->write(bs, data, sizeof(data));
|
||||
}
|
||||
|
||||
void BinarySink_put_uint64(BinarySink *bs, uint64_t val)
|
||||
{
|
||||
unsigned char data[8];
|
||||
PUT_64BIT_MSB_FIRST(data, val);
|
||||
bs->write(bs, data, sizeof(data));
|
||||
}
|
||||
|
||||
void BinarySink_put_string(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
/* Check that the string length fits in a uint32, without doing a
|
||||
* potentially implementation-defined shift of more than 31 bits */
|
||||
assert((len >> 31) < 2);
|
||||
|
||||
BinarySink_put_uint32(bs, len);
|
||||
bs->write(bs, data, len);
|
||||
}
|
||||
|
||||
void BinarySink_put_stringpl(BinarySink *bs, ptrlen pl)
|
||||
{
|
||||
BinarySink_put_string(bs, pl.ptr, pl.len);
|
||||
}
|
||||
|
||||
void BinarySink_put_stringz(BinarySink *bs, const char *str)
|
||||
{
|
||||
BinarySink_put_string(bs, str, strlen(str));
|
||||
}
|
||||
|
||||
void BinarySink_put_stringsb(BinarySink *bs, struct strbuf *buf)
|
||||
{
|
||||
BinarySink_put_string(bs, buf->s, buf->len);
|
||||
strbuf_free(buf);
|
||||
}
|
||||
|
||||
void BinarySink_put_asciz(BinarySink *bs, const char *str)
|
||||
{
|
||||
bs->write(bs, str, strlen(str) + 1);
|
||||
}
|
||||
|
||||
bool BinarySink_put_pstring(BinarySink *bs, const char *str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
if (len > 255)
|
||||
return false; /* can't write a Pascal-style string this long */
|
||||
BinarySink_put_byte(bs, len);
|
||||
bs->write(bs, str, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static bool BinarySource_data_avail(BinarySource *src, size_t wanted)
|
||||
{
|
||||
if (src->err)
|
||||
return false;
|
||||
|
||||
if (wanted <= src->len - src->pos)
|
||||
return true;
|
||||
|
||||
src->err = BSE_OUT_OF_DATA;
|
||||
return false;
|
||||
}
|
||||
|
||||
#define avail(wanted) BinarySource_data_avail(src, wanted)
|
||||
#define advance(dist) (src->pos += dist)
|
||||
#define here ((const void *)((const unsigned char *)src->data + src->pos))
|
||||
#define consume(dist) \
|
||||
((const void *)((const unsigned char *)src->data + \
|
||||
((src->pos += dist) - dist)))
|
||||
|
||||
ptrlen BinarySource_get_data(BinarySource *src, size_t wanted)
|
||||
{
|
||||
if (!avail(wanted))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
return make_ptrlen(consume(wanted), wanted);
|
||||
}
|
||||
|
||||
unsigned char BinarySource_get_byte(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(1))
|
||||
return 0;
|
||||
|
||||
ucp = consume(1);
|
||||
return *ucp;
|
||||
}
|
||||
|
||||
bool BinarySource_get_bool(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(1))
|
||||
return false;
|
||||
|
||||
ucp = consume(1);
|
||||
return *ucp != 0;
|
||||
}
|
||||
|
||||
unsigned BinarySource_get_uint16(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(2))
|
||||
return 0;
|
||||
|
||||
ucp = consume(2);
|
||||
return GET_16BIT_MSB_FIRST(ucp);
|
||||
}
|
||||
|
||||
unsigned long BinarySource_get_uint32(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(4))
|
||||
return 0;
|
||||
|
||||
ucp = consume(4);
|
||||
return GET_32BIT_MSB_FIRST(ucp);
|
||||
}
|
||||
|
||||
uint64_t BinarySource_get_uint64(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(8))
|
||||
return 0;
|
||||
|
||||
ucp = consume(8);
|
||||
return GET_64BIT_MSB_FIRST(ucp);
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_string(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
size_t len;
|
||||
|
||||
if (!avail(4))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
ucp = consume(4);
|
||||
len = GET_32BIT_MSB_FIRST(ucp);
|
||||
|
||||
if (!avail(len))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
return make_ptrlen(consume(len), len);
|
||||
}
|
||||
|
||||
const char *BinarySource_get_asciz(BinarySource *src)
|
||||
{
|
||||
const char *start, *end;
|
||||
|
||||
if (src->err)
|
||||
return "";
|
||||
|
||||
start = here;
|
||||
end = memchr(start, '\0', src->len - src->pos);
|
||||
if (!end) {
|
||||
src->err = BSE_OUT_OF_DATA;
|
||||
return "";
|
||||
}
|
||||
|
||||
advance(end + 1 - start);
|
||||
return start;
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_pstring(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
size_t len;
|
||||
|
||||
if (!avail(1))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
ucp = consume(1);
|
||||
len = *ucp;
|
||||
|
||||
if (!avail(len))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
return make_ptrlen(consume(len), len);
|
||||
}
|
||||
|
||||
static void stdio_sink_write(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
stdio_sink *sink = BinarySink_DOWNCAST(bs, stdio_sink);
|
||||
fwrite(data, 1, len, sink->fp);
|
||||
}
|
||||
|
||||
void stdio_sink_init(stdio_sink *sink, FILE *fp)
|
||||
{
|
||||
sink->fp = fp;
|
||||
BinarySink_INIT(sink, stdio_sink_write);
|
||||
}
|
||||
|
||||
static void bufchain_sink_write(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
bufchain_sink *sink = BinarySink_DOWNCAST(bs, bufchain_sink);
|
||||
bufchain_add(sink->ch, data, len);
|
||||
}
|
||||
|
||||
void bufchain_sink_init(bufchain_sink *sink, bufchain *ch)
|
||||
{
|
||||
sink->ch = ch;
|
||||
BinarySink_INIT(sink, bufchain_sink_write);
|
||||
}
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "marshal.h"
|
||||
#include "misc.h"
|
||||
|
||||
void BinarySink_put_data(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
bs->write(bs, data, len);
|
||||
}
|
||||
|
||||
void BinarySink_put_datapl(BinarySink *bs, ptrlen pl)
|
||||
{
|
||||
BinarySink_put_data(bs, pl.ptr, pl.len);
|
||||
}
|
||||
|
||||
void BinarySink_put_padding(BinarySink *bs, size_t len, unsigned char padbyte)
|
||||
{
|
||||
char buf[16];
|
||||
memset(buf, padbyte, sizeof(buf));
|
||||
while (len > 0) {
|
||||
size_t thislen = len < sizeof(buf) ? len : sizeof(buf);
|
||||
bs->write(bs, buf, thislen);
|
||||
len -= thislen;
|
||||
}
|
||||
}
|
||||
|
||||
void BinarySink_put_byte(BinarySink *bs, unsigned char val)
|
||||
{
|
||||
bs->write(bs, &val, 1);
|
||||
}
|
||||
|
||||
void BinarySink_put_bool(BinarySink *bs, bool val)
|
||||
{
|
||||
unsigned char cval = val ? 1 : 0;
|
||||
bs->write(bs, &cval, 1);
|
||||
}
|
||||
|
||||
void BinarySink_put_uint16(BinarySink *bs, unsigned long val)
|
||||
{
|
||||
unsigned char data[2];
|
||||
PUT_16BIT_MSB_FIRST(data, val);
|
||||
bs->write(bs, data, sizeof(data));
|
||||
}
|
||||
|
||||
void BinarySink_put_uint32(BinarySink *bs, unsigned long val)
|
||||
{
|
||||
unsigned char data[4];
|
||||
PUT_32BIT_MSB_FIRST(data, val);
|
||||
bs->write(bs, data, sizeof(data));
|
||||
}
|
||||
|
||||
void BinarySink_put_uint64(BinarySink *bs, uint64_t val)
|
||||
{
|
||||
unsigned char data[8];
|
||||
PUT_64BIT_MSB_FIRST(data, val);
|
||||
bs->write(bs, data, sizeof(data));
|
||||
}
|
||||
|
||||
void BinarySink_put_string(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
/* Check that the string length fits in a uint32, without doing a
|
||||
* potentially implementation-defined shift of more than 31 bits */
|
||||
assert((len >> 31) < 2);
|
||||
|
||||
BinarySink_put_uint32(bs, len);
|
||||
bs->write(bs, data, len);
|
||||
}
|
||||
|
||||
void BinarySink_put_stringpl(BinarySink *bs, ptrlen pl)
|
||||
{
|
||||
BinarySink_put_string(bs, pl.ptr, pl.len);
|
||||
}
|
||||
|
||||
void BinarySink_put_stringz(BinarySink *bs, const char *str)
|
||||
{
|
||||
BinarySink_put_string(bs, str, strlen(str));
|
||||
}
|
||||
|
||||
void BinarySink_put_stringsb(BinarySink *bs, struct strbuf *buf)
|
||||
{
|
||||
BinarySink_put_string(bs, buf->s, buf->len);
|
||||
strbuf_free(buf);
|
||||
}
|
||||
|
||||
void BinarySink_put_asciz(BinarySink *bs, const char *str)
|
||||
{
|
||||
bs->write(bs, str, strlen(str) + 1);
|
||||
}
|
||||
|
||||
bool BinarySink_put_pstring(BinarySink *bs, const char *str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
if (len > 255)
|
||||
return false; /* can't write a Pascal-style string this long */
|
||||
BinarySink_put_byte(bs, len);
|
||||
bs->write(bs, str, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static bool BinarySource_data_avail(BinarySource *src, size_t wanted)
|
||||
{
|
||||
if (src->err)
|
||||
return false;
|
||||
|
||||
if (wanted <= src->len - src->pos)
|
||||
return true;
|
||||
|
||||
src->err = BSE_OUT_OF_DATA;
|
||||
return false;
|
||||
}
|
||||
|
||||
#define avail(wanted) BinarySource_data_avail(src, wanted)
|
||||
#define advance(dist) (src->pos += dist)
|
||||
#define here ((const void *)((const unsigned char *)src->data + src->pos))
|
||||
#define consume(dist) \
|
||||
((const void *)((const unsigned char *)src->data + \
|
||||
((src->pos += dist) - dist)))
|
||||
|
||||
ptrlen BinarySource_get_data(BinarySource *src, size_t wanted)
|
||||
{
|
||||
if (!avail(wanted))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
return make_ptrlen(consume(wanted), wanted);
|
||||
}
|
||||
|
||||
unsigned char BinarySource_get_byte(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(1))
|
||||
return 0;
|
||||
|
||||
ucp = consume(1);
|
||||
return *ucp;
|
||||
}
|
||||
|
||||
bool BinarySource_get_bool(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(1))
|
||||
return false;
|
||||
|
||||
ucp = consume(1);
|
||||
return *ucp != 0;
|
||||
}
|
||||
|
||||
unsigned BinarySource_get_uint16(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(2))
|
||||
return 0;
|
||||
|
||||
ucp = consume(2);
|
||||
return GET_16BIT_MSB_FIRST(ucp);
|
||||
}
|
||||
|
||||
unsigned long BinarySource_get_uint32(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(4))
|
||||
return 0;
|
||||
|
||||
ucp = consume(4);
|
||||
return GET_32BIT_MSB_FIRST(ucp);
|
||||
}
|
||||
|
||||
uint64_t BinarySource_get_uint64(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
|
||||
if (!avail(8))
|
||||
return 0;
|
||||
|
||||
ucp = consume(8);
|
||||
return GET_64BIT_MSB_FIRST(ucp);
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_string(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
size_t len;
|
||||
|
||||
if (!avail(4))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
ucp = consume(4);
|
||||
len = GET_32BIT_MSB_FIRST(ucp);
|
||||
|
||||
if (!avail(len))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
return make_ptrlen(consume(len), len);
|
||||
}
|
||||
|
||||
const char *BinarySource_get_asciz(BinarySource *src)
|
||||
{
|
||||
const char *start, *end;
|
||||
|
||||
if (src->err)
|
||||
return "";
|
||||
|
||||
start = here;
|
||||
end = memchr(start, '\0', src->len - src->pos);
|
||||
if (!end) {
|
||||
src->err = BSE_OUT_OF_DATA;
|
||||
return "";
|
||||
}
|
||||
|
||||
advance(end + 1 - start);
|
||||
return start;
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_pstring(BinarySource *src)
|
||||
{
|
||||
const unsigned char *ucp;
|
||||
size_t len;
|
||||
|
||||
if (!avail(1))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
ucp = consume(1);
|
||||
len = *ucp;
|
||||
|
||||
if (!avail(len))
|
||||
return make_ptrlen("", 0);
|
||||
|
||||
return make_ptrlen(consume(len), len);
|
||||
}
|
||||
|
||||
void BinarySource_REWIND_TO__(BinarySource *src, size_t pos)
|
||||
{
|
||||
if (pos <= src->len) {
|
||||
src->pos = pos;
|
||||
src->err = BSE_NO_ERROR; /* clear any existing error */
|
||||
} else {
|
||||
src->pos = src->len;
|
||||
src->err = BSE_OUT_OF_DATA; /* new error if we rewind out of range */
|
||||
}
|
||||
}
|
||||
|
||||
static void stdio_sink_write(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
stdio_sink *sink = BinarySink_DOWNCAST(bs, stdio_sink);
|
||||
fwrite(data, 1, len, sink->fp);
|
||||
}
|
||||
|
||||
void stdio_sink_init(stdio_sink *sink, FILE *fp)
|
||||
{
|
||||
sink->fp = fp;
|
||||
BinarySink_INIT(sink, stdio_sink_write);
|
||||
}
|
||||
|
||||
static void bufchain_sink_write(BinarySink *bs, const void *data, size_t len)
|
||||
{
|
||||
bufchain_sink *sink = BinarySink_DOWNCAST(bs, bufchain_sink);
|
||||
bufchain_add(sink->ch, data, len);
|
||||
}
|
||||
|
||||
void bufchain_sink_init(bufchain_sink *sink, bufchain *ch)
|
||||
{
|
||||
sink->ch = ch;
|
||||
BinarySink_INIT(sink, bufchain_sink_write);
|
||||
}
|
||||
@@ -1,323 +1,331 @@
|
||||
#ifndef PUTTY_MARSHAL_H
|
||||
#define PUTTY_MARSHAL_H
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* A sort of 'abstract base class' or 'interface' or 'trait' which is
|
||||
* the common feature of all types that want to accept data formatted
|
||||
* using the SSH binary conventions of uint32, string, mpint etc.
|
||||
*/
|
||||
struct BinarySink {
|
||||
void (*write)(BinarySink *sink, const void *data, size_t len);
|
||||
BinarySink *binarysink_;
|
||||
};
|
||||
|
||||
/*
|
||||
* To define a structure type as a valid target for binary formatted
|
||||
* data, put 'BinarySink_IMPLEMENTATION' in its declaration, and when
|
||||
* an instance is set up, use 'BinarySink_INIT' to initialise the
|
||||
* 'base class' state, providing a function pointer to be the
|
||||
* implementation of the write() call above.
|
||||
*/
|
||||
#define BinarySink_IMPLEMENTATION BinarySink binarysink_[1]
|
||||
#define BinarySink_INIT(obj, writefn) \
|
||||
((obj)->binarysink_->write = (writefn), \
|
||||
(obj)->binarysink_->binarysink_ = (obj)->binarysink_)
|
||||
|
||||
/*
|
||||
* To define a larger structure type as a valid BinarySink in such a
|
||||
* way that it will delegate the write method to some other object,
|
||||
* put 'BinarySink_DELEGATE_IMPLEMENTATION' in its declaration, and
|
||||
* when an instance is set up, use 'BinarySink_DELEGATE_INIT' to point
|
||||
* at the object it wants to delegate to.
|
||||
*
|
||||
* In such a delegated structure, you might sometimes want to have the
|
||||
* delegation stop being valid (e.g. it might be delegating to an
|
||||
* object that only sometimes exists). You can null out the delegate
|
||||
* pointer using BinarySink_DELEGATE_CLEAR.
|
||||
*/
|
||||
#define BinarySink_DELEGATE_IMPLEMENTATION BinarySink *binarysink_
|
||||
#define BinarySink_DELEGATE_INIT(obj, othersink) \
|
||||
((obj)->binarysink_ = BinarySink_UPCAST(othersink))
|
||||
#define BinarySink_DELEGATE_CLEAR(obj) ((obj)->binarysink_ = NULL)
|
||||
|
||||
/*
|
||||
* The implementing type's write function will want to downcast its
|
||||
* 'BinarySink *' parameter back to the more specific type. Also,
|
||||
* sometimes you'll want to upcast a pointer to a particular
|
||||
* implementing type into an abstract 'BinarySink *' to pass to
|
||||
* generic subroutines not defined in this file. These macros do that
|
||||
* job.
|
||||
*
|
||||
* Importantly, BinarySink_UPCAST can also be applied to a BinarySink
|
||||
* * itself (and leaves it unchanged). That's achieved by a small
|
||||
* piece of C trickery: implementing structures and the BinarySink
|
||||
* structure itself both contain a field called binarysink_, but in
|
||||
* implementing objects it's a BinarySink[1] whereas in the abstract
|
||||
* type it's a 'BinarySink *' pointing back to the same structure,
|
||||
* meaning that you can say 'foo->binarysink_' in either case and get
|
||||
* a pointer type by different methods.
|
||||
*/
|
||||
#define BinarySink_DOWNCAST(object, type) \
|
||||
TYPECHECK((object) == ((type *)0)->binarysink_, \
|
||||
((type *)(((char *)(object)) - offsetof(type, binarysink_))))
|
||||
#define BinarySink_UPCAST(object) \
|
||||
TYPECHECK((object)->binarysink_ == (BinarySink *)0, \
|
||||
(object)->binarysink_)
|
||||
|
||||
/*
|
||||
* If you structure-copy an object that's implementing BinarySink,
|
||||
* then that tricky self-pointer in its trait subobject will point to
|
||||
* the wrong place. You could call BinarySink_INIT again, but this
|
||||
* macro is terser and does all that's needed to fix up the copied
|
||||
* object.
|
||||
*/
|
||||
#define BinarySink_COPIED(obj) \
|
||||
((obj)->binarysink_->binarysink_ = (obj)->binarysink_)
|
||||
|
||||
/*
|
||||
* The put_* macros are the main client to this system. Any structure
|
||||
* which implements the BinarySink 'trait' is valid for use as the
|
||||
* first parameter of any of these put_* macros.
|
||||
*/
|
||||
|
||||
/* Basic big-endian integer types. */
|
||||
#define put_byte(bs, val) \
|
||||
BinarySink_put_byte(BinarySink_UPCAST(bs), val)
|
||||
#define put_uint16(bs, val) \
|
||||
BinarySink_put_uint16(BinarySink_UPCAST(bs), val)
|
||||
#define put_uint32(bs, val) \
|
||||
BinarySink_put_uint32(BinarySink_UPCAST(bs), val)
|
||||
#define put_uint64(bs, val) \
|
||||
BinarySink_put_uint64(BinarySink_UPCAST(bs), val)
|
||||
|
||||
/* SSH booleans, encoded as a single byte storing either 0 or 1. */
|
||||
#define put_bool(bs, val) \
|
||||
BinarySink_put_bool(BinarySink_UPCAST(bs), val)
|
||||
|
||||
/* SSH strings, with a leading uint32 length field. 'stringz' is a
|
||||
* convenience function that takes an ordinary C zero-terminated
|
||||
* string as input. 'stringsb' takes a strbuf * as input, and
|
||||
* finalises it as a side effect (handy for multi-level marshalling in
|
||||
* which you use these same functions to format an inner blob of data
|
||||
* that then gets wrapped into a string container in an outer one). */
|
||||
#define put_string(bs, val, len) \
|
||||
BinarySink_put_string(BinarySink_UPCAST(bs),val,len)
|
||||
#define put_stringpl(bs, ptrlen) \
|
||||
BinarySink_put_stringpl(BinarySink_UPCAST(bs),ptrlen)
|
||||
#define put_stringz(bs, val) \
|
||||
BinarySink_put_stringz(BinarySink_UPCAST(bs), val)
|
||||
#define put_stringsb(bs, val) \
|
||||
BinarySink_put_stringsb(BinarySink_UPCAST(bs), val)
|
||||
|
||||
/* Other string outputs: 'asciz' emits the string data directly into
|
||||
* the output including the terminating \0, and 'pstring' emits the
|
||||
* string in Pascal style with a leading _one_-byte length field.
|
||||
* pstring can fail if the string is too long. */
|
||||
#define put_asciz(bs, val) \
|
||||
BinarySink_put_asciz(BinarySink_UPCAST(bs), val)
|
||||
#define put_pstring(bs, val) \
|
||||
BinarySink_put_pstring(BinarySink_UPCAST(bs), val)
|
||||
|
||||
/* Multiprecision integers, in both the SSH-1 and SSH-2 formats. */
|
||||
#define put_mp_ssh1(bs, val) \
|
||||
BinarySink_put_mp_ssh1(BinarySink_UPCAST(bs), val)
|
||||
#define put_mp_ssh2(bs, val) \
|
||||
BinarySink_put_mp_ssh2(BinarySink_UPCAST(bs), val)
|
||||
|
||||
/* Padding with a specified byte. */
|
||||
#define put_padding(bs, len, padbyte) \
|
||||
BinarySink_put_padding(BinarySink_UPCAST(bs), len, padbyte)
|
||||
|
||||
/* Fallback: just emit raw data bytes, using a syntax that matches the
|
||||
* rest of these macros. */
|
||||
#define put_data(bs, val, len) \
|
||||
BinarySink_put_data(BinarySink_UPCAST(bs), val, len)
|
||||
#define put_datapl(bs, pl) \
|
||||
BinarySink_put_datapl(BinarySink_UPCAST(bs), pl)
|
||||
|
||||
/*
|
||||
* The underlying real C functions that implement most of those
|
||||
* macros. Generally you won't want to call these directly, because
|
||||
* they have such cumbersome names; you call the wrapper macros above
|
||||
* instead.
|
||||
*
|
||||
* A few functions whose wrapper macros are defined above are actually
|
||||
* declared in other headers, so as to guarantee that the
|
||||
* declaration(s) of their other parameter type(s) are in scope.
|
||||
*/
|
||||
void BinarySink_put_data(BinarySink *, const void *data, size_t len);
|
||||
void BinarySink_put_datapl(BinarySink *, ptrlen);
|
||||
void BinarySink_put_padding(BinarySink *, size_t len, unsigned char padbyte);
|
||||
void BinarySink_put_byte(BinarySink *, unsigned char);
|
||||
void BinarySink_put_bool(BinarySink *, bool);
|
||||
void BinarySink_put_uint16(BinarySink *, unsigned long);
|
||||
void BinarySink_put_uint32(BinarySink *, unsigned long);
|
||||
void BinarySink_put_uint64(BinarySink *, uint64_t);
|
||||
void BinarySink_put_string(BinarySink *, const void *data, size_t len);
|
||||
void BinarySink_put_stringpl(BinarySink *, ptrlen);
|
||||
void BinarySink_put_stringz(BinarySink *, const char *str);
|
||||
struct strbuf;
|
||||
void BinarySink_put_stringsb(BinarySink *, struct strbuf *);
|
||||
void BinarySink_put_asciz(BinarySink *, const char *str);
|
||||
bool BinarySink_put_pstring(BinarySink *, const char *str);
|
||||
void BinarySink_put_mp_ssh1(BinarySink *bs, mp_int *x);
|
||||
void BinarySink_put_mp_ssh2(BinarySink *bs, mp_int *x);
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* A complementary trait structure for _un_-marshalling.
|
||||
*
|
||||
* This structure contains client-visible data fields rather than
|
||||
* methods, because that seemed more useful than leaving it totally
|
||||
* opaque. But it's still got the self-pointer system that will allow
|
||||
* the set of get_* macros to target one of these itself or any other
|
||||
* type that 'derives' from it. So, for example, an SSH packet
|
||||
* structure can act as a BinarySource while also having additional
|
||||
* fields like the packet type.
|
||||
*/
|
||||
typedef enum BinarySourceError {
|
||||
BSE_NO_ERROR,
|
||||
BSE_OUT_OF_DATA,
|
||||
BSE_INVALID
|
||||
} BinarySourceError;
|
||||
struct BinarySource {
|
||||
/*
|
||||
* (data, len) is the data block being decoded. pos is the current
|
||||
* position within the block.
|
||||
*/
|
||||
const void *data;
|
||||
size_t pos, len;
|
||||
|
||||
/*
|
||||
* 'err' indicates whether a decoding error has happened at any
|
||||
* point. Once this has been set to something other than
|
||||
* BSE_NO_ERROR, it shouldn't be changed by any unmarshalling
|
||||
* function. So you can safely do a long sequence of get_foo()
|
||||
* operations and then test err just once at the end, rather than
|
||||
* having to conditionalise every single get.
|
||||
*
|
||||
* The unmarshalling functions should always return some value,
|
||||
* even if a decoding error occurs. Generally on error they'll
|
||||
* return zero (if numeric) or the empty string (if string-based),
|
||||
* or some other appropriate default value for more complicated
|
||||
* types.
|
||||
*
|
||||
* If the usual return value is dynamically allocated (e.g. a
|
||||
* bignum, or a normal C 'char *' string), then the error value is
|
||||
* also dynamic in the same way. So you have to free exactly the
|
||||
* same set of things whether or not there was a decoding error,
|
||||
* which simplifies exit paths - for example, you could call a big
|
||||
* pile of get_foo functions, then put the actual handling of the
|
||||
* results under 'if (!get_err(src))', and then free everything
|
||||
* outside that if.
|
||||
*/
|
||||
BinarySourceError err;
|
||||
|
||||
/*
|
||||
* Self-pointer for the implicit derivation trick, same as
|
||||
* BinarySink above.
|
||||
*/
|
||||
BinarySource *binarysource_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Implementation macros, similar to BinarySink.
|
||||
*/
|
||||
#define BinarySource_IMPLEMENTATION BinarySource binarysource_[1]
|
||||
static inline void BinarySource_INIT__(BinarySource *src, ptrlen data)
|
||||
{
|
||||
src->data = data.ptr;
|
||||
src->len = data.len;
|
||||
src->pos = 0;
|
||||
src->err = BSE_NO_ERROR;
|
||||
src->binarysource_ = src;
|
||||
}
|
||||
#define BinarySource_BARE_INIT_PL(obj, pl) \
|
||||
TYPECHECK(&(obj)->binarysource_ == (BinarySource **)0, \
|
||||
BinarySource_INIT__(obj, pl))
|
||||
#define BinarySource_BARE_INIT(obj, data_, len_) \
|
||||
BinarySource_BARE_INIT_PL(obj, make_ptrlen(data_, len_))
|
||||
#define BinarySource_INIT_PL(obj, pl) \
|
||||
TYPECHECK(&(obj)->binarysource_ == (BinarySource (*)[1])0, \
|
||||
BinarySource_INIT__(BinarySource_UPCAST(obj), pl))
|
||||
#define BinarySource_INIT(obj, data_, len_) \
|
||||
BinarySource_INIT_PL(obj, make_ptrlen(data_, len_))
|
||||
#define BinarySource_DOWNCAST(object, type) \
|
||||
TYPECHECK((object) == ((type *)0)->binarysource_, \
|
||||
((type *)(((char *)(object)) - offsetof(type, binarysource_))))
|
||||
#define BinarySource_UPCAST(object) \
|
||||
TYPECHECK((object)->binarysource_ == (BinarySource *)0, \
|
||||
(object)->binarysource_)
|
||||
#define BinarySource_COPIED(obj) \
|
||||
((obj)->binarysource_->binarysource_ = (obj)->binarysource_)
|
||||
|
||||
#define get_data(src, len) \
|
||||
BinarySource_get_data(BinarySource_UPCAST(src), len)
|
||||
#define get_byte(src) \
|
||||
BinarySource_get_byte(BinarySource_UPCAST(src))
|
||||
#define get_bool(src) \
|
||||
BinarySource_get_bool(BinarySource_UPCAST(src))
|
||||
#define get_uint16(src) \
|
||||
BinarySource_get_uint16(BinarySource_UPCAST(src))
|
||||
#define get_uint32(src) \
|
||||
BinarySource_get_uint32(BinarySource_UPCAST(src))
|
||||
#define get_uint64(src) \
|
||||
BinarySource_get_uint64(BinarySource_UPCAST(src))
|
||||
#define get_string(src) \
|
||||
BinarySource_get_string(BinarySource_UPCAST(src))
|
||||
#define get_asciz(src) \
|
||||
BinarySource_get_asciz(BinarySource_UPCAST(src))
|
||||
#define get_pstring(src) \
|
||||
BinarySource_get_pstring(BinarySource_UPCAST(src))
|
||||
#define get_mp_ssh1(src) \
|
||||
BinarySource_get_mp_ssh1(BinarySource_UPCAST(src))
|
||||
#define get_mp_ssh2(src) \
|
||||
BinarySource_get_mp_ssh2(BinarySource_UPCAST(src))
|
||||
#define get_rsa_ssh1_pub(src, rsa, order) \
|
||||
BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, order)
|
||||
#define get_rsa_ssh1_priv(src, rsa) \
|
||||
BinarySource_get_rsa_ssh1_priv(BinarySource_UPCAST(src), rsa)
|
||||
|
||||
#define get_err(src) (BinarySource_UPCAST(src)->err)
|
||||
#define get_avail(src) (BinarySource_UPCAST(src)->len - \
|
||||
BinarySource_UPCAST(src)->pos)
|
||||
#define get_ptr(src) \
|
||||
((const void *)( \
|
||||
(const unsigned char *)(BinarySource_UPCAST(src)->data) + \
|
||||
BinarySource_UPCAST(src)->pos))
|
||||
|
||||
ptrlen BinarySource_get_data(BinarySource *, size_t);
|
||||
unsigned char BinarySource_get_byte(BinarySource *);
|
||||
bool BinarySource_get_bool(BinarySource *);
|
||||
unsigned BinarySource_get_uint16(BinarySource *);
|
||||
unsigned long BinarySource_get_uint32(BinarySource *);
|
||||
uint64_t BinarySource_get_uint64(BinarySource *);
|
||||
ptrlen BinarySource_get_string(BinarySource *);
|
||||
const char *BinarySource_get_asciz(BinarySource *);
|
||||
ptrlen BinarySource_get_pstring(BinarySource *);
|
||||
mp_int *BinarySource_get_mp_ssh1(BinarySource *src);
|
||||
mp_int *BinarySource_get_mp_ssh2(BinarySource *src);
|
||||
|
||||
/*
|
||||
* A couple of useful standard BinarySink implementations, which live
|
||||
* as sensibly here as anywhere else: one that makes a BinarySink
|
||||
* whose effect is to write to a stdio stream, and one whose effect is
|
||||
* to append to a bufchain.
|
||||
*/
|
||||
struct stdio_sink {
|
||||
FILE *fp;
|
||||
BinarySink_IMPLEMENTATION;
|
||||
};
|
||||
struct bufchain_sink {
|
||||
bufchain *ch;
|
||||
BinarySink_IMPLEMENTATION;
|
||||
};
|
||||
void stdio_sink_init(stdio_sink *sink, FILE *fp);
|
||||
void bufchain_sink_init(bufchain_sink *sink, bufchain *ch);
|
||||
|
||||
#endif /* PUTTY_MARSHAL_H */
|
||||
#ifndef PUTTY_MARSHAL_H
|
||||
#define PUTTY_MARSHAL_H
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* A sort of 'abstract base class' or 'interface' or 'trait' which is
|
||||
* the common feature of all types that want to accept data formatted
|
||||
* using the SSH binary conventions of uint32, string, mpint etc.
|
||||
*/
|
||||
struct BinarySink {
|
||||
void (*write)(BinarySink *sink, const void *data, size_t len);
|
||||
BinarySink *binarysink_;
|
||||
};
|
||||
|
||||
/*
|
||||
* To define a structure type as a valid target for binary formatted
|
||||
* data, put 'BinarySink_IMPLEMENTATION' in its declaration, and when
|
||||
* an instance is set up, use 'BinarySink_INIT' to initialise the
|
||||
* 'base class' state, providing a function pointer to be the
|
||||
* implementation of the write() call above.
|
||||
*/
|
||||
#define BinarySink_IMPLEMENTATION BinarySink binarysink_[1]
|
||||
#define BinarySink_INIT(obj, writefn) \
|
||||
((obj)->binarysink_->write = (writefn), \
|
||||
(obj)->binarysink_->binarysink_ = (obj)->binarysink_)
|
||||
|
||||
/*
|
||||
* To define a larger structure type as a valid BinarySink in such a
|
||||
* way that it will delegate the write method to some other object,
|
||||
* put 'BinarySink_DELEGATE_IMPLEMENTATION' in its declaration, and
|
||||
* when an instance is set up, use 'BinarySink_DELEGATE_INIT' to point
|
||||
* at the object it wants to delegate to.
|
||||
*
|
||||
* In such a delegated structure, you might sometimes want to have the
|
||||
* delegation stop being valid (e.g. it might be delegating to an
|
||||
* object that only sometimes exists). You can null out the delegate
|
||||
* pointer using BinarySink_DELEGATE_CLEAR.
|
||||
*/
|
||||
#define BinarySink_DELEGATE_IMPLEMENTATION BinarySink *binarysink_
|
||||
#define BinarySink_DELEGATE_INIT(obj, othersink) \
|
||||
((obj)->binarysink_ = BinarySink_UPCAST(othersink))
|
||||
#define BinarySink_DELEGATE_CLEAR(obj) ((obj)->binarysink_ = NULL)
|
||||
|
||||
/*
|
||||
* The implementing type's write function will want to downcast its
|
||||
* 'BinarySink *' parameter back to the more specific type. Also,
|
||||
* sometimes you'll want to upcast a pointer to a particular
|
||||
* implementing type into an abstract 'BinarySink *' to pass to
|
||||
* generic subroutines not defined in this file. These macros do that
|
||||
* job.
|
||||
*
|
||||
* Importantly, BinarySink_UPCAST can also be applied to a BinarySink
|
||||
* * itself (and leaves it unchanged). That's achieved by a small
|
||||
* piece of C trickery: implementing structures and the BinarySink
|
||||
* structure itself both contain a field called binarysink_, but in
|
||||
* implementing objects it's a BinarySink[1] whereas in the abstract
|
||||
* type it's a 'BinarySink *' pointing back to the same structure,
|
||||
* meaning that you can say 'foo->binarysink_' in either case and get
|
||||
* a pointer type by different methods.
|
||||
*/
|
||||
#define BinarySink_DOWNCAST(object, type) \
|
||||
TYPECHECK((object) == ((type *)0)->binarysink_, \
|
||||
((type *)(((char *)(object)) - offsetof(type, binarysink_))))
|
||||
#define BinarySink_UPCAST(object) \
|
||||
TYPECHECK((object)->binarysink_ == (BinarySink *)0, \
|
||||
(object)->binarysink_)
|
||||
|
||||
/*
|
||||
* If you structure-copy an object that's implementing BinarySink,
|
||||
* then that tricky self-pointer in its trait subobject will point to
|
||||
* the wrong place. You could call BinarySink_INIT again, but this
|
||||
* macro is terser and does all that's needed to fix up the copied
|
||||
* object.
|
||||
*/
|
||||
#define BinarySink_COPIED(obj) \
|
||||
((obj)->binarysink_->binarysink_ = (obj)->binarysink_)
|
||||
|
||||
/*
|
||||
* The put_* macros are the main client to this system. Any structure
|
||||
* which implements the BinarySink 'trait' is valid for use as the
|
||||
* first parameter of any of these put_* macros.
|
||||
*/
|
||||
|
||||
/* Basic big-endian integer types. */
|
||||
#define put_byte(bs, val) \
|
||||
BinarySink_put_byte(BinarySink_UPCAST(bs), val)
|
||||
#define put_uint16(bs, val) \
|
||||
BinarySink_put_uint16(BinarySink_UPCAST(bs), val)
|
||||
#define put_uint32(bs, val) \
|
||||
BinarySink_put_uint32(BinarySink_UPCAST(bs), val)
|
||||
#define put_uint64(bs, val) \
|
||||
BinarySink_put_uint64(BinarySink_UPCAST(bs), val)
|
||||
|
||||
/* SSH booleans, encoded as a single byte storing either 0 or 1. */
|
||||
#define put_bool(bs, val) \
|
||||
BinarySink_put_bool(BinarySink_UPCAST(bs), val)
|
||||
|
||||
/* SSH strings, with a leading uint32 length field. 'stringz' is a
|
||||
* convenience function that takes an ordinary C zero-terminated
|
||||
* string as input. 'stringsb' takes a strbuf * as input, and
|
||||
* finalises it as a side effect (handy for multi-level marshalling in
|
||||
* which you use these same functions to format an inner blob of data
|
||||
* that then gets wrapped into a string container in an outer one). */
|
||||
#define put_string(bs, val, len) \
|
||||
BinarySink_put_string(BinarySink_UPCAST(bs),val,len)
|
||||
#define put_stringpl(bs, ptrlen) \
|
||||
BinarySink_put_stringpl(BinarySink_UPCAST(bs),ptrlen)
|
||||
#define put_stringz(bs, val) \
|
||||
BinarySink_put_stringz(BinarySink_UPCAST(bs), val)
|
||||
#define put_stringsb(bs, val) \
|
||||
BinarySink_put_stringsb(BinarySink_UPCAST(bs), val)
|
||||
|
||||
/* Other string outputs: 'asciz' emits the string data directly into
|
||||
* the output including the terminating \0, and 'pstring' emits the
|
||||
* string in Pascal style with a leading _one_-byte length field.
|
||||
* pstring can fail if the string is too long. */
|
||||
#define put_asciz(bs, val) \
|
||||
BinarySink_put_asciz(BinarySink_UPCAST(bs), val)
|
||||
#define put_pstring(bs, val) \
|
||||
BinarySink_put_pstring(BinarySink_UPCAST(bs), val)
|
||||
|
||||
/* Multiprecision integers, in both the SSH-1 and SSH-2 formats. */
|
||||
#define put_mp_ssh1(bs, val) \
|
||||
BinarySink_put_mp_ssh1(BinarySink_UPCAST(bs), val)
|
||||
#define put_mp_ssh2(bs, val) \
|
||||
BinarySink_put_mp_ssh2(BinarySink_UPCAST(bs), val)
|
||||
|
||||
/* Padding with a specified byte. */
|
||||
#define put_padding(bs, len, padbyte) \
|
||||
BinarySink_put_padding(BinarySink_UPCAST(bs), len, padbyte)
|
||||
|
||||
/* Fallback: just emit raw data bytes, using a syntax that matches the
|
||||
* rest of these macros. */
|
||||
#define put_data(bs, val, len) \
|
||||
BinarySink_put_data(BinarySink_UPCAST(bs), val, len)
|
||||
#define put_datapl(bs, pl) \
|
||||
BinarySink_put_datapl(BinarySink_UPCAST(bs), pl)
|
||||
|
||||
/*
|
||||
* The underlying real C functions that implement most of those
|
||||
* macros. Generally you won't want to call these directly, because
|
||||
* they have such cumbersome names; you call the wrapper macros above
|
||||
* instead.
|
||||
*
|
||||
* A few functions whose wrapper macros are defined above are actually
|
||||
* declared in other headers, so as to guarantee that the
|
||||
* declaration(s) of their other parameter type(s) are in scope.
|
||||
*/
|
||||
void BinarySink_put_data(BinarySink *, const void *data, size_t len);
|
||||
void BinarySink_put_datapl(BinarySink *, ptrlen);
|
||||
void BinarySink_put_padding(BinarySink *, size_t len, unsigned char padbyte);
|
||||
void BinarySink_put_byte(BinarySink *, unsigned char);
|
||||
void BinarySink_put_bool(BinarySink *, bool);
|
||||
void BinarySink_put_uint16(BinarySink *, unsigned long);
|
||||
void BinarySink_put_uint32(BinarySink *, unsigned long);
|
||||
void BinarySink_put_uint64(BinarySink *, uint64_t);
|
||||
void BinarySink_put_string(BinarySink *, const void *data, size_t len);
|
||||
void BinarySink_put_stringpl(BinarySink *, ptrlen);
|
||||
void BinarySink_put_stringz(BinarySink *, const char *str);
|
||||
struct strbuf;
|
||||
void BinarySink_put_stringsb(BinarySink *, struct strbuf *);
|
||||
void BinarySink_put_asciz(BinarySink *, const char *str);
|
||||
bool BinarySink_put_pstring(BinarySink *, const char *str);
|
||||
void BinarySink_put_mp_ssh1(BinarySink *bs, mp_int *x);
|
||||
void BinarySink_put_mp_ssh2(BinarySink *bs, mp_int *x);
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* A complementary trait structure for _un_-marshalling.
|
||||
*
|
||||
* This structure contains client-visible data fields rather than
|
||||
* methods, because that seemed more useful than leaving it totally
|
||||
* opaque. But it's still got the self-pointer system that will allow
|
||||
* the set of get_* macros to target one of these itself or any other
|
||||
* type that 'derives' from it. So, for example, an SSH packet
|
||||
* structure can act as a BinarySource while also having additional
|
||||
* fields like the packet type.
|
||||
*/
|
||||
typedef enum BinarySourceError {
|
||||
BSE_NO_ERROR,
|
||||
BSE_OUT_OF_DATA,
|
||||
BSE_INVALID
|
||||
} BinarySourceError;
|
||||
struct BinarySource {
|
||||
/*
|
||||
* (data, len) is the data block being decoded. pos is the current
|
||||
* position within the block.
|
||||
*/
|
||||
const void *data;
|
||||
size_t pos, len;
|
||||
|
||||
/*
|
||||
* 'err' indicates whether a decoding error has happened at any
|
||||
* point. Once this has been set to something other than
|
||||
* BSE_NO_ERROR, it shouldn't be changed by any unmarshalling
|
||||
* function. So you can safely do a long sequence of get_foo()
|
||||
* operations and then test err just once at the end, rather than
|
||||
* having to conditionalise every single get.
|
||||
*
|
||||
* The unmarshalling functions should always return some value,
|
||||
* even if a decoding error occurs. Generally on error they'll
|
||||
* return zero (if numeric) or the empty string (if string-based),
|
||||
* or some other appropriate default value for more complicated
|
||||
* types.
|
||||
*
|
||||
* If the usual return value is dynamically allocated (e.g. a
|
||||
* bignum, or a normal C 'char *' string), then the error value is
|
||||
* also dynamic in the same way. So you have to free exactly the
|
||||
* same set of things whether or not there was a decoding error,
|
||||
* which simplifies exit paths - for example, you could call a big
|
||||
* pile of get_foo functions, then put the actual handling of the
|
||||
* results under 'if (!get_err(src))', and then free everything
|
||||
* outside that if.
|
||||
*/
|
||||
BinarySourceError err;
|
||||
|
||||
/*
|
||||
* Self-pointer for the implicit derivation trick, same as
|
||||
* BinarySink above.
|
||||
*/
|
||||
BinarySource *binarysource_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Implementation macros, similar to BinarySink.
|
||||
*/
|
||||
#define BinarySource_IMPLEMENTATION BinarySource binarysource_[1]
|
||||
static inline void BinarySource_INIT__(BinarySource *src, ptrlen data)
|
||||
{
|
||||
src->data = data.ptr;
|
||||
src->len = data.len;
|
||||
src->pos = 0;
|
||||
src->err = BSE_NO_ERROR;
|
||||
src->binarysource_ = src;
|
||||
}
|
||||
#define BinarySource_BARE_INIT_PL(obj, pl) \
|
||||
TYPECHECK(&(obj)->binarysource_ == (BinarySource **)0, \
|
||||
BinarySource_INIT__(obj, pl))
|
||||
#define BinarySource_BARE_INIT(obj, data_, len_) \
|
||||
BinarySource_BARE_INIT_PL(obj, make_ptrlen(data_, len_))
|
||||
#define BinarySource_INIT_PL(obj, pl) \
|
||||
TYPECHECK(&(obj)->binarysource_ == (BinarySource (*)[1])0, \
|
||||
BinarySource_INIT__(BinarySource_UPCAST(obj), pl))
|
||||
#define BinarySource_INIT(obj, data_, len_) \
|
||||
BinarySource_INIT_PL(obj, make_ptrlen(data_, len_))
|
||||
#define BinarySource_DOWNCAST(object, type) \
|
||||
TYPECHECK((object) == ((type *)0)->binarysource_, \
|
||||
((type *)(((char *)(object)) - offsetof(type, binarysource_))))
|
||||
#define BinarySource_UPCAST(object) \
|
||||
TYPECHECK((object)->binarysource_ == (BinarySource *)0, \
|
||||
(object)->binarysource_)
|
||||
#define BinarySource_COPIED(obj) \
|
||||
((obj)->binarysource_->binarysource_ = (obj)->binarysource_)
|
||||
#define BinarySource_REWIND_TO(src, pos) \
|
||||
BinarySource_REWIND_TO__((src)->binarysource_, pos)
|
||||
#define BinarySource_REWIND(src) \
|
||||
BinarySource_REWIND_TO__((src)->binarysource_, 0)
|
||||
|
||||
#define get_data(src, len) \
|
||||
BinarySource_get_data(BinarySource_UPCAST(src), len)
|
||||
#define get_byte(src) \
|
||||
BinarySource_get_byte(BinarySource_UPCAST(src))
|
||||
#define get_bool(src) \
|
||||
BinarySource_get_bool(BinarySource_UPCAST(src))
|
||||
#define get_uint16(src) \
|
||||
BinarySource_get_uint16(BinarySource_UPCAST(src))
|
||||
#define get_uint32(src) \
|
||||
BinarySource_get_uint32(BinarySource_UPCAST(src))
|
||||
#define get_uint64(src) \
|
||||
BinarySource_get_uint64(BinarySource_UPCAST(src))
|
||||
#define get_string(src) \
|
||||
BinarySource_get_string(BinarySource_UPCAST(src))
|
||||
#define get_asciz(src) \
|
||||
BinarySource_get_asciz(BinarySource_UPCAST(src))
|
||||
#define get_pstring(src) \
|
||||
BinarySource_get_pstring(BinarySource_UPCAST(src))
|
||||
#define get_mp_ssh1(src) \
|
||||
BinarySource_get_mp_ssh1(BinarySource_UPCAST(src))
|
||||
#define get_mp_ssh2(src) \
|
||||
BinarySource_get_mp_ssh2(BinarySource_UPCAST(src))
|
||||
#define get_rsa_ssh1_pub(src, rsa, order) \
|
||||
BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, order)
|
||||
#define get_rsa_ssh1_priv(src, rsa) \
|
||||
BinarySource_get_rsa_ssh1_priv(BinarySource_UPCAST(src), rsa)
|
||||
#define get_rsa_ssh1_priv_agent(src) \
|
||||
BinarySource_get_rsa_ssh1_priv_agent(BinarySource_UPCAST(src))
|
||||
|
||||
#define get_err(src) (BinarySource_UPCAST(src)->err)
|
||||
#define get_avail(src) (BinarySource_UPCAST(src)->len - \
|
||||
BinarySource_UPCAST(src)->pos)
|
||||
#define get_ptr(src) \
|
||||
((const void *)( \
|
||||
(const unsigned char *)(BinarySource_UPCAST(src)->data) + \
|
||||
BinarySource_UPCAST(src)->pos))
|
||||
|
||||
ptrlen BinarySource_get_data(BinarySource *, size_t);
|
||||
unsigned char BinarySource_get_byte(BinarySource *);
|
||||
bool BinarySource_get_bool(BinarySource *);
|
||||
unsigned BinarySource_get_uint16(BinarySource *);
|
||||
unsigned long BinarySource_get_uint32(BinarySource *);
|
||||
uint64_t BinarySource_get_uint64(BinarySource *);
|
||||
ptrlen BinarySource_get_string(BinarySource *);
|
||||
const char *BinarySource_get_asciz(BinarySource *);
|
||||
ptrlen BinarySource_get_pstring(BinarySource *);
|
||||
mp_int *BinarySource_get_mp_ssh1(BinarySource *src);
|
||||
mp_int *BinarySource_get_mp_ssh2(BinarySource *src);
|
||||
|
||||
void BinarySource_REWIND_TO__(BinarySource *src, size_t pos);
|
||||
|
||||
/*
|
||||
* A couple of useful standard BinarySink implementations, which live
|
||||
* as sensibly here as anywhere else: one that makes a BinarySink
|
||||
* whose effect is to write to a stdio stream, and one whose effect is
|
||||
* to append to a bufchain.
|
||||
*/
|
||||
struct stdio_sink {
|
||||
FILE *fp;
|
||||
BinarySink_IMPLEMENTATION;
|
||||
};
|
||||
struct bufchain_sink {
|
||||
bufchain *ch;
|
||||
BinarySink_IMPLEMENTATION;
|
||||
};
|
||||
void stdio_sink_init(stdio_sink *sink, FILE *fp);
|
||||
void bufchain_sink_init(bufchain_sink *sink, bufchain *ch);
|
||||
|
||||
#endif /* PUTTY_MARSHAL_H */
|
||||
@@ -46,22 +46,22 @@ void *saferealloc(void *ptr, size_t n, size_t size)
|
||||
void *p;
|
||||
|
||||
if (n > INT_MAX / size) {
|
||||
p = NULL;
|
||||
p = NULL;
|
||||
} else {
|
||||
size *= n;
|
||||
if (!ptr) {
|
||||
size *= n;
|
||||
if (!ptr) {
|
||||
#ifdef MINEFIELD
|
||||
p = minefield_c_malloc(size);
|
||||
p = minefield_c_malloc(size);
|
||||
#else
|
||||
p = malloc(size);
|
||||
p = malloc(size);
|
||||
#endif
|
||||
} else {
|
||||
} else {
|
||||
#ifdef MINEFIELD
|
||||
p = minefield_c_realloc(ptr, size);
|
||||
p = minefield_c_realloc(ptr, size);
|
||||
#else
|
||||
p = realloc(ptr, size);
|
||||
p = realloc(ptr, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!p)
|
||||
@@ -74,9 +74,9 @@ void safefree(void *ptr)
|
||||
{
|
||||
if (ptr) {
|
||||
#ifdef MINEFIELD
|
||||
minefield_c_free(ptr);
|
||||
minefield_c_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -121,9 +121,11 @@ void *safegrowarray(void *ptr, size_t *allocated, size_t eltsize,
|
||||
void *toret;
|
||||
if (secret) {
|
||||
toret = safemalloc(newsize, eltsize, 0);
|
||||
memcpy(toret, ptr, oldsize * eltsize);
|
||||
smemclr(ptr, oldsize * eltsize);
|
||||
sfree(ptr);
|
||||
if (oldsize) {
|
||||
memcpy(toret, ptr, oldsize * eltsize);
|
||||
smemclr(ptr, oldsize * eltsize);
|
||||
sfree(ptr);
|
||||
}
|
||||
} else {
|
||||
toret = saferealloc(ptr, newsize, eltsize);
|
||||
}
|
||||
@@ -1,389 +1,380 @@
|
||||
/*
|
||||
* Platform-independent routines shared between all PuTTY programs.
|
||||
*
|
||||
* This file contains functions that use the kind of infrastructure
|
||||
* like conf.c that tends to only live in the main applications, or
|
||||
* that do things that only something like a main PuTTY application
|
||||
* would need. So standalone test programs should generally be able to
|
||||
* avoid linking against it.
|
||||
*
|
||||
* More standalone functions that depend on nothing but the C library
|
||||
* live in utils.c.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "putty.h"
|
||||
#include "misc.h"
|
||||
|
||||
void seat_connection_fatal(Seat *seat, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *msg;
|
||||
|
||||
va_start(ap, fmt);
|
||||
msg = dupvprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
seat->vt->connection_fatal(seat, msg);
|
||||
sfree(msg); /* if we return */
|
||||
}
|
||||
|
||||
prompts_t *new_prompts(void)
|
||||
{
|
||||
prompts_t *p = snew(prompts_t);
|
||||
p->prompts = NULL;
|
||||
p->n_prompts = p->prompts_size = 0;
|
||||
p->data = NULL;
|
||||
p->to_server = true; /* to be on the safe side */
|
||||
p->name = p->instruction = NULL;
|
||||
p->name_reqd = p->instr_reqd = false;
|
||||
return p;
|
||||
}
|
||||
void add_prompt(prompts_t *p, char *promptstr, bool echo)
|
||||
{
|
||||
prompt_t *pr = snew(prompt_t);
|
||||
pr->prompt = promptstr;
|
||||
pr->echo = echo;
|
||||
pr->result = NULL;
|
||||
pr->resultsize = 0;
|
||||
sgrowarray(p->prompts, p->prompts_size, p->n_prompts);
|
||||
p->prompts[p->n_prompts++] = pr;
|
||||
}
|
||||
void prompt_ensure_result_size(prompt_t *pr, int newlen)
|
||||
{
|
||||
if ((int)pr->resultsize < newlen) {
|
||||
char *newbuf;
|
||||
newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */
|
||||
|
||||
/*
|
||||
* We don't use sresize / realloc here, because we will be
|
||||
* storing sensitive stuff like passwords in here, and we want
|
||||
* to make sure that the data doesn't get copied around in
|
||||
* memory without the old copy being destroyed.
|
||||
*/
|
||||
newbuf = snewn(newlen, char);
|
||||
memcpy(newbuf, pr->result, pr->resultsize);
|
||||
smemclr(pr->result, pr->resultsize);
|
||||
sfree(pr->result);
|
||||
pr->result = newbuf;
|
||||
pr->resultsize = newlen;
|
||||
}
|
||||
}
|
||||
void prompt_set_result(prompt_t *pr, const char *newstr)
|
||||
{
|
||||
prompt_ensure_result_size(pr, strlen(newstr) + 1);
|
||||
strcpy(pr->result, newstr);
|
||||
}
|
||||
void free_prompts(prompts_t *p)
|
||||
{
|
||||
size_t i;
|
||||
for (i=0; i < p->n_prompts; i++) {
|
||||
prompt_t *pr = p->prompts[i];
|
||||
smemclr(pr->result, pr->resultsize); /* burn the evidence */
|
||||
sfree(pr->result);
|
||||
sfree(pr->prompt);
|
||||
sfree(pr);
|
||||
}
|
||||
sfree(p->prompts);
|
||||
sfree(p->name);
|
||||
sfree(p->instruction);
|
||||
sfree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine whether or not a Conf represents a session which can
|
||||
* sensibly be launched right now.
|
||||
*/
|
||||
bool conf_launchable(Conf *conf)
|
||||
{
|
||||
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
|
||||
return conf_get_str(conf, CONF_serline)[0] != 0;
|
||||
else
|
||||
return conf_get_str(conf, CONF_host)[0] != 0;
|
||||
}
|
||||
|
||||
char const *conf_dest(Conf *conf)
|
||||
{
|
||||
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
|
||||
return conf_get_str(conf, CONF_serline);
|
||||
else
|
||||
return conf_get_str(conf, CONF_host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate a manual host key specification (either entered in the
|
||||
* GUI, or via -hostkey). If valid, we return true, and update 'key'
|
||||
* to contain a canonicalised version of the key string in 'key'
|
||||
* (which is guaranteed to take up at most as much space as the
|
||||
* original version), suitable for putting into the Conf. If not
|
||||
* valid, we return false.
|
||||
*/
|
||||
bool validate_manual_hostkey(char *key)
|
||||
{
|
||||
char *p, *q, *r, *s;
|
||||
|
||||
/*
|
||||
* Step through the string word by word, looking for a word that's
|
||||
* in one of the formats we like.
|
||||
*/
|
||||
p = key;
|
||||
while ((p += strspn(p, " \t"))[0]) {
|
||||
q = p;
|
||||
p += strcspn(p, " \t");
|
||||
if (*p) *p++ = '\0';
|
||||
|
||||
/*
|
||||
* Now q is our word.
|
||||
*/
|
||||
|
||||
if (strlen(q) == 16*3 - 1 &&
|
||||
q[strspn(q, "0123456789abcdefABCDEF:")] == 0) {
|
||||
/*
|
||||
* Might be a key fingerprint. Check the colons are in the
|
||||
* right places, and if so, return the same fingerprint
|
||||
* canonicalised into lowercase.
|
||||
*/
|
||||
int i;
|
||||
for (i = 0; i < 16; i++)
|
||||
if (q[3*i] == ':' || q[3*i+1] == ':')
|
||||
goto not_fingerprint; /* sorry */
|
||||
for (i = 0; i < 15; i++)
|
||||
if (q[3*i+2] != ':')
|
||||
goto not_fingerprint; /* sorry */
|
||||
for (i = 0; i < 16*3 - 1; i++)
|
||||
key[i] = tolower(q[i]);
|
||||
key[16*3 - 1] = '\0';
|
||||
return true;
|
||||
}
|
||||
not_fingerprint:;
|
||||
|
||||
/*
|
||||
* Before we check for a public-key blob, trim newlines out of
|
||||
* the middle of the word, in case someone's managed to paste
|
||||
* in a public-key blob _with_ them.
|
||||
*/
|
||||
for (r = s = q; *r; r++)
|
||||
if (*r != '\n' && *r != '\r')
|
||||
*s++ = *r;
|
||||
*s = '\0';
|
||||
|
||||
if (strlen(q) % 4 == 0 && strlen(q) > 2*4 &&
|
||||
q[strspn(q, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz+/=")] == 0) {
|
||||
/*
|
||||
* Might be a base64-encoded SSH-2 public key blob. Check
|
||||
* that it starts with a sensible algorithm string. No
|
||||
* canonicalisation is necessary for this string type.
|
||||
*
|
||||
* The algorithm string must be at most 64 characters long
|
||||
* (RFC 4251 section 6).
|
||||
*/
|
||||
unsigned char decoded[6];
|
||||
unsigned alglen;
|
||||
int minlen;
|
||||
int len = 0;
|
||||
|
||||
len += base64_decode_atom(q, decoded+len);
|
||||
if (len < 3)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
len += base64_decode_atom(q+4, decoded+len);
|
||||
if (len < 4)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
|
||||
alglen = GET_32BIT_MSB_FIRST(decoded);
|
||||
if (alglen > 64)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
|
||||
minlen = ((alglen + 4) + 2) / 3;
|
||||
if (strlen(q) < minlen)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
|
||||
strcpy(key, q);
|
||||
return true;
|
||||
}
|
||||
not_ssh2_blob:;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char *buildinfo(const char *newline)
|
||||
{
|
||||
strbuf *buf = strbuf_new();
|
||||
|
||||
strbuf_catf(buf, "Build platform: %d-bit %s",
|
||||
(int)(CHAR_BIT * sizeof(void *)),
|
||||
BUILDINFO_PLATFORM);
|
||||
|
||||
#ifdef __clang_version__
|
||||
#define FOUND_COMPILER
|
||||
strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__);
|
||||
#elif defined __GNUC__ && defined __VERSION__
|
||||
#define FOUND_COMPILER
|
||||
strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__);
|
||||
#endif
|
||||
|
||||
#if defined _MSC_VER
|
||||
#ifndef FOUND_COMPILER
|
||||
#define FOUND_COMPILER
|
||||
strbuf_catf(buf, "%sCompiler: ", newline);
|
||||
#else
|
||||
strbuf_catf(buf, ", emulating ");
|
||||
#endif
|
||||
strbuf_catf(buf, "Visual Studio", newline);
|
||||
#if 0
|
||||
/*
|
||||
* List of _MSC_VER values and their translations taken from
|
||||
* https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
|
||||
* except for 1920, which is not yet listed on that page as of
|
||||
* 2019-03-22, and was determined experimentally by Sean Kain.
|
||||
*
|
||||
* The pointless #if 0 branch containing this comment is there so
|
||||
* that every real clause can start with #elif and there's no
|
||||
* anomalous first clause. That way the patch looks nicer when you
|
||||
* add extra ones.
|
||||
*/
|
||||
#elif _MSC_VER == 1920
|
||||
strbuf_catf(buf, " 2019 (16.x)");
|
||||
#elif _MSC_VER == 1916
|
||||
strbuf_catf(buf, " 2017 version 15.9");
|
||||
#elif _MSC_VER == 1915
|
||||
strbuf_catf(buf, " 2017 version 15.8");
|
||||
#elif _MSC_VER == 1914
|
||||
strbuf_catf(buf, " 2017 version 15.7");
|
||||
#elif _MSC_VER == 1913
|
||||
strbuf_catf(buf, " 2017 version 15.6");
|
||||
#elif _MSC_VER == 1912
|
||||
strbuf_catf(buf, " 2017 version 15.5");
|
||||
#elif _MSC_VER == 1911
|
||||
strbuf_catf(buf, " 2017 version 15.3");
|
||||
#elif _MSC_VER == 1910
|
||||
strbuf_catf(buf, " 2017 RTW (15.0)");
|
||||
#elif _MSC_VER == 1900
|
||||
strbuf_catf(buf, " 2015 (14.0)");
|
||||
#elif _MSC_VER == 1800
|
||||
strbuf_catf(buf, " 2013 (12.0)");
|
||||
#elif _MSC_VER == 1700
|
||||
strbuf_catf(buf, " 2012 (11.0)");
|
||||
#elif _MSC_VER == 1600
|
||||
strbuf_catf(buf, " 2010 (10.0)");
|
||||
#elif _MSC_VER == 1500
|
||||
strbuf_catf(buf, " 2008 (9.0)");
|
||||
#elif _MSC_VER == 1400
|
||||
strbuf_catf(buf, " 2005 (8.0)");
|
||||
#elif _MSC_VER == 1310
|
||||
strbuf_catf(buf, " .NET 2003 (7.1)");
|
||||
#elif _MSC_VER == 1300
|
||||
strbuf_catf(buf, " .NET 2002 (7.0)");
|
||||
#elif _MSC_VER == 1200
|
||||
strbuf_catf(buf, " 6.0");
|
||||
#else
|
||||
strbuf_catf(buf, ", unrecognised version");
|
||||
#endif
|
||||
strbuf_catf(buf, ", _MSC_VER=%d", (int)_MSC_VER);
|
||||
#endif
|
||||
|
||||
#ifdef BUILDINFO_GTK
|
||||
{
|
||||
char *gtk_buildinfo = buildinfo_gtk_version();
|
||||
if (gtk_buildinfo) {
|
||||
strbuf_catf(buf, "%sCompiled against GTK version %s",
|
||||
newline, gtk_buildinfo);
|
||||
sfree(gtk_buildinfo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined _WINDOWS
|
||||
{
|
||||
int echm = has_embedded_chm();
|
||||
if (echm >= 0)
|
||||
strbuf_catf(buf, "%sEmbedded HTML Help file: %s", newline,
|
||||
echm ? "yes" : "no");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined _WINDOWS && defined MINEFIELD
|
||||
strbuf_catf(buf, "%sBuild option: MINEFIELD", newline);
|
||||
#endif
|
||||
#ifdef NO_SECURITY
|
||||
strbuf_catf(buf, "%sBuild option: NO_SECURITY", newline);
|
||||
#endif
|
||||
#ifdef NO_SECUREZEROMEMORY
|
||||
strbuf_catf(buf, "%sBuild option: NO_SECUREZEROMEMORY", newline);
|
||||
#endif
|
||||
#ifdef NO_IPV6
|
||||
strbuf_catf(buf, "%sBuild option: NO_IPV6", newline);
|
||||
#endif
|
||||
#ifdef NO_GSSAPI
|
||||
strbuf_catf(buf, "%sBuild option: NO_GSSAPI", newline);
|
||||
#endif
|
||||
#ifdef STATIC_GSSAPI
|
||||
strbuf_catf(buf, "%sBuild option: STATIC_GSSAPI", newline);
|
||||
#endif
|
||||
#ifdef UNPROTECT
|
||||
strbuf_catf(buf, "%sBuild option: UNPROTECT", newline);
|
||||
#endif
|
||||
#ifdef FUZZING
|
||||
strbuf_catf(buf, "%sBuild option: FUZZING", newline);
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
strbuf_catf(buf, "%sBuild option: DEBUG", newline);
|
||||
#endif
|
||||
|
||||
strbuf_catf(buf, "%sSource commit: %s", newline, commitid);
|
||||
|
||||
return strbuf_to_str(buf);
|
||||
}
|
||||
|
||||
size_t nullseat_output(
|
||||
Seat *seat, bool is_stderr, const void *data, size_t len) { return 0; }
|
||||
bool nullseat_eof(Seat *seat) { return true; }
|
||||
int nullseat_get_userpass_input(
|
||||
Seat *seat, prompts_t *p, bufchain *input) { return 0; }
|
||||
void nullseat_notify_remote_exit(Seat *seat) {}
|
||||
void nullseat_connection_fatal(Seat *seat, const char *message) {}
|
||||
void nullseat_update_specials_menu(Seat *seat) {}
|
||||
char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; }
|
||||
void nullseat_set_busy_status(Seat *seat, BusyStatus status) {}
|
||||
int nullseat_verify_ssh_host_key(
|
||||
Seat *seat, const char *host, int port,
|
||||
const char *keytype, char *keystr, char *key_fingerprint,
|
||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||
int nullseat_confirm_weak_crypto_primitive(
|
||||
Seat *seat, const char *algtype, const char *algname,
|
||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||
int nullseat_confirm_weak_cached_hostkey(
|
||||
Seat *seat, const char *algname, const char *betteralgs,
|
||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||
bool nullseat_is_never_utf8(Seat *seat) { return false; }
|
||||
bool nullseat_is_always_utf8(Seat *seat) { return true; }
|
||||
void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing) {}
|
||||
const char *nullseat_get_x_display(Seat *seat) { return NULL; }
|
||||
bool nullseat_get_windowid(Seat *seat, long *id_out) { return false; }
|
||||
bool nullseat_get_window_pixel_size(
|
||||
Seat *seat, int *width, int *height) { return false; }
|
||||
StripCtrlChars *nullseat_stripctrl_new(
|
||||
Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) {return NULL;}
|
||||
bool nullseat_set_trust_status(Seat *seat, bool tr) { return false; }
|
||||
bool nullseat_set_trust_status_vacuously(Seat *seat, bool tr) { return true; }
|
||||
|
||||
void sk_free_peer_info(SocketPeerInfo *pi)
|
||||
{
|
||||
if (pi) {
|
||||
sfree((char *)pi->addr_text);
|
||||
sfree((char *)pi->log_text);
|
||||
sfree(pi);
|
||||
}
|
||||
}
|
||||
|
||||
void out_of_memory(void)
|
||||
{
|
||||
modalfatalbox("Out of memory");
|
||||
}
|
||||
/*
|
||||
* Platform-independent routines shared between all PuTTY programs.
|
||||
*
|
||||
* This file contains functions that use the kind of infrastructure
|
||||
* like conf.c that tends to only live in the main applications, or
|
||||
* that do things that only something like a main PuTTY application
|
||||
* would need. So standalone test programs should generally be able to
|
||||
* avoid linking against it.
|
||||
*
|
||||
* More standalone functions that depend on nothing but the C library
|
||||
* live in utils.c.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "putty.h"
|
||||
#include "misc.h"
|
||||
|
||||
void seat_connection_fatal(Seat *seat, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *msg;
|
||||
|
||||
va_start(ap, fmt);
|
||||
msg = dupvprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
seat->vt->connection_fatal(seat, msg);
|
||||
sfree(msg); /* if we return */
|
||||
}
|
||||
|
||||
prompts_t *new_prompts(void)
|
||||
{
|
||||
prompts_t *p = snew(prompts_t);
|
||||
p->prompts = NULL;
|
||||
p->n_prompts = p->prompts_size = 0;
|
||||
p->data = NULL;
|
||||
p->to_server = true; /* to be on the safe side */
|
||||
p->name = p->instruction = NULL;
|
||||
p->name_reqd = p->instr_reqd = false;
|
||||
return p;
|
||||
}
|
||||
void add_prompt(prompts_t *p, char *promptstr, bool echo)
|
||||
{
|
||||
prompt_t *pr = snew(prompt_t);
|
||||
pr->prompt = promptstr;
|
||||
pr->echo = echo;
|
||||
pr->result = strbuf_new_nm();
|
||||
sgrowarray(p->prompts, p->prompts_size, p->n_prompts);
|
||||
p->prompts[p->n_prompts++] = pr;
|
||||
}
|
||||
void prompt_set_result(prompt_t *pr, const char *newstr)
|
||||
{
|
||||
strbuf_clear(pr->result);
|
||||
put_datapl(pr->result, ptrlen_from_asciz(newstr));
|
||||
}
|
||||
const char *prompt_get_result_ref(prompt_t *pr)
|
||||
{
|
||||
return pr->result->s;
|
||||
}
|
||||
char *prompt_get_result(prompt_t *pr)
|
||||
{
|
||||
return dupstr(pr->result->s);
|
||||
}
|
||||
void free_prompts(prompts_t *p)
|
||||
{
|
||||
size_t i;
|
||||
for (i=0; i < p->n_prompts; i++) {
|
||||
prompt_t *pr = p->prompts[i];
|
||||
strbuf_free(pr->result);
|
||||
sfree(pr->prompt);
|
||||
sfree(pr);
|
||||
}
|
||||
sfree(p->prompts);
|
||||
sfree(p->name);
|
||||
sfree(p->instruction);
|
||||
sfree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine whether or not a Conf represents a session which can
|
||||
* sensibly be launched right now.
|
||||
*/
|
||||
bool conf_launchable(Conf *conf)
|
||||
{
|
||||
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
|
||||
return conf_get_str(conf, CONF_serline)[0] != 0;
|
||||
else
|
||||
return conf_get_str(conf, CONF_host)[0] != 0;
|
||||
}
|
||||
|
||||
char const *conf_dest(Conf *conf)
|
||||
{
|
||||
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
|
||||
return conf_get_str(conf, CONF_serline);
|
||||
else
|
||||
return conf_get_str(conf, CONF_host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate a manual host key specification (either entered in the
|
||||
* GUI, or via -hostkey). If valid, we return true, and update 'key'
|
||||
* to contain a canonicalised version of the key string in 'key'
|
||||
* (which is guaranteed to take up at most as much space as the
|
||||
* original version), suitable for putting into the Conf. If not
|
||||
* valid, we return false.
|
||||
*/
|
||||
bool validate_manual_hostkey(char *key)
|
||||
{
|
||||
char *p, *q, *r, *s;
|
||||
|
||||
/*
|
||||
* Step through the string word by word, looking for a word that's
|
||||
* in one of the formats we like.
|
||||
*/
|
||||
p = key;
|
||||
while ((p += strspn(p, " \t"))[0]) {
|
||||
q = p;
|
||||
p += strcspn(p, " \t");
|
||||
if (*p) *p++ = '\0';
|
||||
|
||||
/*
|
||||
* Now q is our word.
|
||||
*/
|
||||
|
||||
if (strlen(q) == 16*3 - 1 &&
|
||||
q[strspn(q, "0123456789abcdefABCDEF:")] == 0) {
|
||||
/*
|
||||
* Might be a key fingerprint. Check the colons are in the
|
||||
* right places, and if so, return the same fingerprint
|
||||
* canonicalised into lowercase.
|
||||
*/
|
||||
int i;
|
||||
for (i = 0; i < 16; i++)
|
||||
if (q[3*i] == ':' || q[3*i+1] == ':')
|
||||
goto not_fingerprint; /* sorry */
|
||||
for (i = 0; i < 15; i++)
|
||||
if (q[3*i+2] != ':')
|
||||
goto not_fingerprint; /* sorry */
|
||||
for (i = 0; i < 16*3 - 1; i++)
|
||||
key[i] = tolower(q[i]);
|
||||
key[16*3 - 1] = '\0';
|
||||
return true;
|
||||
}
|
||||
not_fingerprint:;
|
||||
|
||||
/*
|
||||
* Before we check for a public-key blob, trim newlines out of
|
||||
* the middle of the word, in case someone's managed to paste
|
||||
* in a public-key blob _with_ them.
|
||||
*/
|
||||
for (r = s = q; *r; r++)
|
||||
if (*r != '\n' && *r != '\r')
|
||||
*s++ = *r;
|
||||
*s = '\0';
|
||||
|
||||
if (strlen(q) % 4 == 0 && strlen(q) > 2*4 &&
|
||||
q[strspn(q, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz+/=")] == 0) {
|
||||
/*
|
||||
* Might be a base64-encoded SSH-2 public key blob. Check
|
||||
* that it starts with a sensible algorithm string. No
|
||||
* canonicalisation is necessary for this string type.
|
||||
*
|
||||
* The algorithm string must be at most 64 characters long
|
||||
* (RFC 4251 section 6).
|
||||
*/
|
||||
unsigned char decoded[6];
|
||||
unsigned alglen;
|
||||
int minlen;
|
||||
int len = 0;
|
||||
|
||||
len += base64_decode_atom(q, decoded+len);
|
||||
if (len < 3)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
len += base64_decode_atom(q+4, decoded+len);
|
||||
if (len < 4)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
|
||||
alglen = GET_32BIT_MSB_FIRST(decoded);
|
||||
if (alglen > 64)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
|
||||
minlen = ((alglen + 4) + 2) / 3;
|
||||
if (strlen(q) < minlen)
|
||||
goto not_ssh2_blob; /* sorry */
|
||||
|
||||
strcpy(key, q);
|
||||
return true;
|
||||
}
|
||||
not_ssh2_blob:;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char *buildinfo(const char *newline)
|
||||
{
|
||||
strbuf *buf = strbuf_new();
|
||||
|
||||
strbuf_catf(buf, "Build platform: %d-bit %s",
|
||||
(int)(CHAR_BIT * sizeof(void *)),
|
||||
BUILDINFO_PLATFORM);
|
||||
|
||||
#ifdef __clang_version__
|
||||
#define FOUND_COMPILER
|
||||
strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__);
|
||||
#elif defined __GNUC__ && defined __VERSION__
|
||||
#define FOUND_COMPILER
|
||||
strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__);
|
||||
#endif
|
||||
|
||||
#if defined _MSC_VER
|
||||
#ifndef FOUND_COMPILER
|
||||
#define FOUND_COMPILER
|
||||
strbuf_catf(buf, "%sCompiler: ", newline);
|
||||
#else
|
||||
strbuf_catf(buf, ", emulating ");
|
||||
#endif
|
||||
strbuf_catf(buf, "Visual Studio");
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* List of _MSC_VER values and their translations taken from
|
||||
* https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
|
||||
*
|
||||
* The pointless #if 0 branch containing this comment is there so
|
||||
* that every real clause can start with #elif and there's no
|
||||
* anomalous first clause. That way the patch looks nicer when you
|
||||
* add extra ones.
|
||||
*/
|
||||
#elif _MSC_VER == 1923
|
||||
strbuf_catf(buf, " 2019 (16.3)");
|
||||
#elif _MSC_VER == 1922
|
||||
strbuf_catf(buf, " 2019 (16.2)");
|
||||
#elif _MSC_VER == 1921
|
||||
strbuf_catf(buf, " 2019 (16.1)");
|
||||
#elif _MSC_VER == 1920
|
||||
strbuf_catf(buf, " 2019 (16.0)");
|
||||
#elif _MSC_VER == 1916
|
||||
strbuf_catf(buf, " 2017 version 15.9");
|
||||
#elif _MSC_VER == 1915
|
||||
strbuf_catf(buf, " 2017 version 15.8");
|
||||
#elif _MSC_VER == 1914
|
||||
strbuf_catf(buf, " 2017 version 15.7");
|
||||
#elif _MSC_VER == 1913
|
||||
strbuf_catf(buf, " 2017 version 15.6");
|
||||
#elif _MSC_VER == 1912
|
||||
strbuf_catf(buf, " 2017 version 15.5");
|
||||
#elif _MSC_VER == 1911
|
||||
strbuf_catf(buf, " 2017 version 15.3");
|
||||
#elif _MSC_VER == 1910
|
||||
strbuf_catf(buf, " 2017 RTW (15.0)");
|
||||
#elif _MSC_VER == 1900
|
||||
strbuf_catf(buf, " 2015 (14.0)");
|
||||
#elif _MSC_VER == 1800
|
||||
strbuf_catf(buf, " 2013 (12.0)");
|
||||
#elif _MSC_VER == 1700
|
||||
strbuf_catf(buf, " 2012 (11.0)");
|
||||
#elif _MSC_VER == 1600
|
||||
strbuf_catf(buf, " 2010 (10.0)");
|
||||
#elif _MSC_VER == 1500
|
||||
strbuf_catf(buf, " 2008 (9.0)");
|
||||
#elif _MSC_VER == 1400
|
||||
strbuf_catf(buf, " 2005 (8.0)");
|
||||
#elif _MSC_VER == 1310
|
||||
strbuf_catf(buf, " .NET 2003 (7.1)");
|
||||
#elif _MSC_VER == 1300
|
||||
strbuf_catf(buf, " .NET 2002 (7.0)");
|
||||
#elif _MSC_VER == 1200
|
||||
strbuf_catf(buf, " 6.0");
|
||||
#else
|
||||
strbuf_catf(buf, ", unrecognised version");
|
||||
#endif
|
||||
strbuf_catf(buf, ", _MSC_VER=%d", (int)_MSC_VER);
|
||||
#endif
|
||||
|
||||
#ifdef BUILDINFO_GTK
|
||||
{
|
||||
char *gtk_buildinfo = buildinfo_gtk_version();
|
||||
if (gtk_buildinfo) {
|
||||
strbuf_catf(buf, "%sCompiled against GTK version %s",
|
||||
newline, gtk_buildinfo);
|
||||
sfree(gtk_buildinfo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined _WINDOWS
|
||||
{
|
||||
int echm = has_embedded_chm();
|
||||
if (echm >= 0)
|
||||
strbuf_catf(buf, "%sEmbedded HTML Help file: %s", newline,
|
||||
echm ? "yes" : "no");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined _WINDOWS && defined MINEFIELD
|
||||
strbuf_catf(buf, "%sBuild option: MINEFIELD", newline);
|
||||
#endif
|
||||
#ifdef NO_SECURITY
|
||||
strbuf_catf(buf, "%sBuild option: NO_SECURITY", newline);
|
||||
#endif
|
||||
#ifdef NO_SECUREZEROMEMORY
|
||||
strbuf_catf(buf, "%sBuild option: NO_SECUREZEROMEMORY", newline);
|
||||
#endif
|
||||
#ifdef NO_IPV6
|
||||
strbuf_catf(buf, "%sBuild option: NO_IPV6", newline);
|
||||
#endif
|
||||
#ifdef NO_GSSAPI
|
||||
strbuf_catf(buf, "%sBuild option: NO_GSSAPI", newline);
|
||||
#endif
|
||||
#ifdef STATIC_GSSAPI
|
||||
strbuf_catf(buf, "%sBuild option: STATIC_GSSAPI", newline);
|
||||
#endif
|
||||
#ifdef UNPROTECT
|
||||
strbuf_catf(buf, "%sBuild option: UNPROTECT", newline);
|
||||
#endif
|
||||
#ifdef FUZZING
|
||||
strbuf_catf(buf, "%sBuild option: FUZZING", newline);
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
strbuf_catf(buf, "%sBuild option: DEBUG", newline);
|
||||
#endif
|
||||
|
||||
strbuf_catf(buf, "%sSource commit: %s", newline, commitid);
|
||||
|
||||
return strbuf_to_str(buf);
|
||||
}
|
||||
|
||||
size_t nullseat_output(
|
||||
Seat *seat, bool is_stderr, const void *data, size_t len) { return 0; }
|
||||
bool nullseat_eof(Seat *seat) { return true; }
|
||||
int nullseat_get_userpass_input(
|
||||
Seat *seat, prompts_t *p, bufchain *input) { return 0; }
|
||||
void nullseat_notify_remote_exit(Seat *seat) {}
|
||||
void nullseat_connection_fatal(Seat *seat, const char *message) {}
|
||||
void nullseat_update_specials_menu(Seat *seat) {}
|
||||
char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; }
|
||||
void nullseat_set_busy_status(Seat *seat, BusyStatus status) {}
|
||||
int nullseat_verify_ssh_host_key(
|
||||
Seat *seat, const char *host, int port,
|
||||
const char *keytype, char *keystr, char *key_fingerprint,
|
||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||
int nullseat_confirm_weak_crypto_primitive(
|
||||
Seat *seat, const char *algtype, const char *algname,
|
||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||
int nullseat_confirm_weak_cached_hostkey(
|
||||
Seat *seat, const char *algname, const char *betteralgs,
|
||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||
bool nullseat_is_never_utf8(Seat *seat) { return false; }
|
||||
bool nullseat_is_always_utf8(Seat *seat) { return true; }
|
||||
void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing) {}
|
||||
const char *nullseat_get_x_display(Seat *seat) { return NULL; }
|
||||
bool nullseat_get_windowid(Seat *seat, long *id_out) { return false; }
|
||||
bool nullseat_get_window_pixel_size(
|
||||
Seat *seat, int *width, int *height) { return false; }
|
||||
StripCtrlChars *nullseat_stripctrl_new(
|
||||
Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) {return NULL;}
|
||||
bool nullseat_set_trust_status(Seat *seat, bool tr) { return false; }
|
||||
bool nullseat_set_trust_status_vacuously(Seat *seat, bool tr) { return true; }
|
||||
|
||||
void sk_free_peer_info(SocketPeerInfo *pi)
|
||||
{
|
||||
if (pi) {
|
||||
sfree((char *)pi->addr_text);
|
||||
sfree((char *)pi->log_text);
|
||||
sfree(pi);
|
||||
}
|
||||
}
|
||||
|
||||
void out_of_memory(void)
|
||||
{
|
||||
modalfatalbox("Out of memory");
|
||||
}
|
||||
@@ -1,420 +1,405 @@
|
||||
/*
|
||||
* Header for misc.c.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_MISC_H
|
||||
#define PUTTY_MISC_H
|
||||
|
||||
#include "defs.h"
|
||||
#include "puttymem.h"
|
||||
#include "marshal.h"
|
||||
|
||||
#include <stdio.h> /* for FILE * */
|
||||
#include <stdarg.h> /* for va_list */
|
||||
#include <stdlib.h> /* for abort */
|
||||
#include <time.h> /* for struct tm */
|
||||
#include <limits.h> /* for INT_MAX/MIN */
|
||||
#include <assert.h> /* for assert (obviously) */
|
||||
|
||||
unsigned long parse_blocksize(const char *bs);
|
||||
char ctrlparse(char *s, char **next);
|
||||
|
||||
size_t host_strcspn(const char *s, const char *set);
|
||||
char *host_strchr(const char *s, int c);
|
||||
char *host_strrchr(const char *s, int c);
|
||||
char *host_strduptrim(const char *s);
|
||||
|
||||
#ifdef __GNUC__
|
||||
/*
|
||||
* On MinGW, the correct compiler format checking for vsnprintf() etc
|
||||
* can depend on compile-time flags; these control whether you get
|
||||
* ISO C or Microsoft's non-standard format strings.
|
||||
* We sometimes use __attribute__ ((format)) for our own printf-like
|
||||
* functions, which are ultimately interpreted by the toolchain-chosen
|
||||
* printf, so we need to take that into account to get correct warnings.
|
||||
*/
|
||||
#ifdef __MINGW_PRINTF_FORMAT
|
||||
#define PUTTY_PRINTF_ARCHETYPE __MINGW_PRINTF_FORMAT
|
||||
#else
|
||||
#define PUTTY_PRINTF_ARCHETYPE printf
|
||||
#endif
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
char *dupstr(const char *s);
|
||||
char *dupcat(const char *s1, ...);
|
||||
char *dupprintf(const char *fmt, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((format (PUTTY_PRINTF_ARCHETYPE, 1, 2)))
|
||||
#endif
|
||||
;
|
||||
char *dupvprintf(const char *fmt, va_list ap);
|
||||
void burnstr(char *string);
|
||||
|
||||
/*
|
||||
* The visible part of a strbuf structure. There's a surrounding
|
||||
* implementation struct in misc.c, which isn't exposed to client
|
||||
* code.
|
||||
*/
|
||||
struct strbuf {
|
||||
char *s;
|
||||
unsigned char *u;
|
||||
size_t len;
|
||||
BinarySink_IMPLEMENTATION;
|
||||
};
|
||||
|
||||
/* strbuf constructors: strbuf_new_nm and strbuf_new differ in that a
|
||||
* strbuf constructed using the _nm version will resize itself by
|
||||
* alloc/copy/smemclr/free instead of realloc. Use that version for
|
||||
* data sensitive enough that it's worth costing performance to
|
||||
* avoid copies of it lingering in process memory. */
|
||||
strbuf *strbuf_new(void);
|
||||
strbuf *strbuf_new_nm(void);
|
||||
|
||||
void strbuf_free(strbuf *buf);
|
||||
void *strbuf_append(strbuf *buf, size_t len);
|
||||
char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */
|
||||
void strbuf_catf(strbuf *buf, const char *fmt, ...);
|
||||
void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap);
|
||||
|
||||
strbuf *strbuf_new_for_agent_query(void);
|
||||
void strbuf_finalise_agent_query(strbuf *buf);
|
||||
|
||||
/* String-to-Unicode converters that auto-allocate the destination and
|
||||
* work around the rather deficient interface of mb_to_wc.
|
||||
*
|
||||
* These actually live in miscucs.c, not misc.c (the distinction being
|
||||
* that the former is only linked into tools that also have the main
|
||||
* Unicode support). */
|
||||
wchar_t *dup_mb_to_wc_c(int codepage, int flags, const char *string, int len);
|
||||
wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string);
|
||||
|
||||
static inline int toint(unsigned u)
|
||||
{
|
||||
/*
|
||||
* Convert an unsigned to an int, without running into the
|
||||
* undefined behaviour which happens by the strict C standard if
|
||||
* the value overflows. You'd hope that sensible compilers would
|
||||
* do the sensible thing in response to a cast, but actually I
|
||||
* don't trust modern compilers not to do silly things like
|
||||
* assuming that _obviously_ you wouldn't have caused an overflow
|
||||
* and so they can elide an 'if (i < 0)' test immediately after
|
||||
* the cast.
|
||||
*
|
||||
* Sensible compilers ought of course to optimise this entire
|
||||
* function into 'just return the input value', and since it's
|
||||
* also declared inline, elide it completely in their output.
|
||||
*/
|
||||
if (u <= (unsigned)INT_MAX)
|
||||
return (int)u;
|
||||
else if (u >= (unsigned)INT_MIN) /* wrap in cast _to_ unsigned is OK */
|
||||
return INT_MIN + (int)(u - (unsigned)INT_MIN);
|
||||
else
|
||||
return INT_MIN; /* fallback; should never occur on binary machines */
|
||||
}
|
||||
|
||||
char *fgetline(FILE *fp);
|
||||
bool read_file_into(BinarySink *bs, FILE *fp);
|
||||
char *chomp(char *str);
|
||||
bool strstartswith(const char *s, const char *t);
|
||||
bool strendswith(const char *s, const char *t);
|
||||
|
||||
void base64_encode_atom(const unsigned char *data, int n, char *out);
|
||||
int base64_decode_atom(const char *atom, unsigned char *out);
|
||||
|
||||
struct bufchain_granule;
|
||||
struct bufchain_tag {
|
||||
struct bufchain_granule *head, *tail;
|
||||
size_t buffersize; /* current amount of buffered data */
|
||||
|
||||
void (*queue_idempotent_callback)(IdempotentCallback *ic);
|
||||
IdempotentCallback *ic;
|
||||
};
|
||||
|
||||
void bufchain_init(bufchain *ch);
|
||||
void bufchain_clear(bufchain *ch);
|
||||
size_t bufchain_size(bufchain *ch);
|
||||
void bufchain_add(bufchain *ch, const void *data, size_t len);
|
||||
ptrlen bufchain_prefix(bufchain *ch);
|
||||
void bufchain_consume(bufchain *ch, size_t len);
|
||||
void bufchain_fetch(bufchain *ch, void *data, size_t len);
|
||||
void bufchain_fetch_consume(bufchain *ch, void *data, size_t len);
|
||||
bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len);
|
||||
size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len);
|
||||
void bufchain_set_callback_inner(
|
||||
bufchain *ch, IdempotentCallback *ic,
|
||||
void (*queue_idempotent_callback)(IdempotentCallback *ic));
|
||||
static inline void bufchain_set_callback(bufchain *ch, IdempotentCallback *ic)
|
||||
{
|
||||
extern void queue_idempotent_callback(struct IdempotentCallback *ic);
|
||||
/* Wrapper that puts in the standard queue_idempotent_callback
|
||||
* function. Lives here rather than in utils.c so that standalone
|
||||
* programs can use the bufchain facility without this optional
|
||||
* callback feature and not need to provide a stub of
|
||||
* queue_idempotent_callback. */
|
||||
bufchain_set_callback_inner(ch, ic, queue_idempotent_callback);
|
||||
}
|
||||
|
||||
bool validate_manual_hostkey(char *key);
|
||||
|
||||
struct tm ltime(void);
|
||||
|
||||
/*
|
||||
* Special form of strcmp which can cope with NULL inputs. NULL is
|
||||
* defined to sort before even the empty string.
|
||||
*/
|
||||
int nullstrcmp(const char *a, const char *b);
|
||||
|
||||
static inline ptrlen make_ptrlen(const void *ptr, size_t len)
|
||||
{
|
||||
ptrlen pl;
|
||||
pl.ptr = ptr;
|
||||
pl.len = len;
|
||||
return pl;
|
||||
}
|
||||
|
||||
static inline ptrlen ptrlen_from_asciz(const char *str)
|
||||
{
|
||||
return make_ptrlen(str, strlen(str));
|
||||
}
|
||||
|
||||
static inline ptrlen ptrlen_from_strbuf(strbuf *sb)
|
||||
{
|
||||
return make_ptrlen(sb->u, sb->len);
|
||||
}
|
||||
|
||||
bool ptrlen_eq_string(ptrlen pl, const char *str);
|
||||
bool ptrlen_eq_ptrlen(ptrlen pl1, ptrlen pl2);
|
||||
int ptrlen_strcmp(ptrlen pl1, ptrlen pl2);
|
||||
/* ptrlen_startswith and ptrlen_endswith write through their 'tail'
|
||||
* argument if and only if it is non-NULL and they return true. Hence
|
||||
* you can write ptrlen_startswith(thing, prefix, &thing), writing
|
||||
* back to the same ptrlen it read from, to remove a prefix if present
|
||||
* and say whether it did so. */
|
||||
bool ptrlen_startswith(ptrlen whole, ptrlen prefix, ptrlen *tail);
|
||||
bool ptrlen_endswith(ptrlen whole, ptrlen suffix, ptrlen *tail);
|
||||
ptrlen ptrlen_get_word(ptrlen *input, const char *separators);
|
||||
char *mkstr(ptrlen pl);
|
||||
int string_length_for_printf(size_t);
|
||||
/* Derive two printf arguments from a ptrlen, suitable for "%.*s" */
|
||||
#define PTRLEN_PRINTF(pl) \
|
||||
string_length_for_printf((pl).len), (const char *)(pl).ptr
|
||||
/* Make a ptrlen out of a compile-time string literal. We try to
|
||||
* enforce that it _is_ a string literal by token-pasting "" on to it,
|
||||
* which should provoke a compile error if it's any other kind of
|
||||
* string. */
|
||||
#define PTRLEN_LITERAL(stringlit) \
|
||||
TYPECHECK("" stringlit "", make_ptrlen(stringlit, sizeof(stringlit)-1))
|
||||
/* Make a ptrlen out of a constant byte array. */
|
||||
#define PTRLEN_FROM_CONST_BYTES(a) make_ptrlen(a, sizeof(a))
|
||||
|
||||
/* Wipe sensitive data out of memory that's about to be freed. Simpler
|
||||
* than memset because we don't need the fill char parameter; also
|
||||
* attempts (by fiddly use of volatile) to inhibit the compiler from
|
||||
* over-cleverly trying to optimise the memset away because it knows
|
||||
* the variable is going out of scope. */
|
||||
void smemclr(void *b, size_t len);
|
||||
|
||||
/* Compare two fixed-length chunks of memory for equality, without
|
||||
* data-dependent control flow (so an attacker with a very accurate
|
||||
* stopwatch can't try to guess where the first mismatching byte was).
|
||||
* Returns false for mismatch or true for equality (unlike memcmp),
|
||||
* hinted at by the 'eq' in the name. */
|
||||
bool smemeq(const void *av, const void *bv, size_t len);
|
||||
|
||||
/* Encode a single UTF-8 character. Assumes that illegal characters
|
||||
* (such as things in the surrogate range, or > 0x10FFFF) have already
|
||||
* been removed. */
|
||||
size_t encode_utf8(void *output, unsigned long ch);
|
||||
|
||||
char *buildinfo(const char *newline);
|
||||
|
||||
/*
|
||||
* A function you can put at points in the code where execution should
|
||||
* never reach in the first place. Better than assert(false), or even
|
||||
* assert(false && "some explanatory message"), because some compilers
|
||||
* don't interpret assert(false) as a declaration of unreachability,
|
||||
* so they may still warn about pointless things like some variable
|
||||
* not being initialised on the unreachable code path.
|
||||
*
|
||||
* I follow the assertion with a call to abort() just in case someone
|
||||
* compiles with -DNDEBUG, and I wrap that abort inside my own
|
||||
* function labelled NORETURN just in case some unusual kind of system
|
||||
* header wasn't foresighted enough to label abort() itself that way.
|
||||
*/
|
||||
static inline NORETURN void unreachable_internal(void) { abort(); }
|
||||
#define unreachable(msg) (assert(false && msg), unreachable_internal())
|
||||
|
||||
/*
|
||||
* Debugging functions.
|
||||
*
|
||||
* Output goes to debug.log
|
||||
*
|
||||
* debug() is like printf().
|
||||
*
|
||||
* dmemdump() and dmemdumpl() both do memory dumps. The difference
|
||||
* is that dmemdumpl() is more suited for when the memory address is
|
||||
* important (say because you'll be recording pointer values later
|
||||
* on). dmemdump() is more concise.
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
void debug_printf(const char *fmt, ...);
|
||||
void debug_memdump(const void *buf, int len, bool L);
|
||||
#define debug(...) (debug_printf(__VA_ARGS__))
|
||||
#define dmemdump(buf,len) (debug_memdump(buf, len, false))
|
||||
#define dmemdumpl(buf,len) (debug_memdump(buf, len, true))
|
||||
#else
|
||||
#define debug(...) ((void)0)
|
||||
#define dmemdump(buf,len) ((void)0)
|
||||
#define dmemdumpl(buf,len) ((void)0)
|
||||
#endif
|
||||
|
||||
#ifndef lenof
|
||||
#define lenof(x) ( (sizeof((x))) / (sizeof(*(x))))
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
#define min(x,y) ( (x) < (y) ? (x) : (y) )
|
||||
#endif
|
||||
#ifndef max
|
||||
#define max(x,y) ( (x) > (y) ? (x) : (y) )
|
||||
#endif
|
||||
|
||||
static inline uint64_t GET_64BIT_LSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint64_t)p[0] ) | ((uint64_t)p[1] << 8) |
|
||||
((uint64_t)p[2] << 16) | ((uint64_t)p[3] << 24) |
|
||||
((uint64_t)p[4] << 32) | ((uint64_t)p[5] << 40) |
|
||||
((uint64_t)p[6] << 48) | ((uint64_t)p[7] << 56));
|
||||
}
|
||||
|
||||
static inline void PUT_64BIT_LSB_FIRST(void *vp, uint64_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (uint8_t)(value);
|
||||
p[1] = (uint8_t)(value >> 8);
|
||||
p[2] = (uint8_t)(value >> 16);
|
||||
p[3] = (uint8_t)(value >> 24);
|
||||
p[4] = (uint8_t)(value >> 32);
|
||||
p[5] = (uint8_t)(value >> 40);
|
||||
p[6] = (uint8_t)(value >> 48);
|
||||
p[7] = (uint8_t)(value >> 56);
|
||||
}
|
||||
|
||||
static inline uint32_t GET_32BIT_LSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint32_t)p[0] ) | ((uint32_t)p[1] << 8) |
|
||||
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24));
|
||||
}
|
||||
|
||||
static inline void PUT_32BIT_LSB_FIRST(void *vp, uint32_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (uint8_t)(value);
|
||||
p[1] = (uint8_t)(value >> 8);
|
||||
p[2] = (uint8_t)(value >> 16);
|
||||
p[3] = (uint8_t)(value >> 24);
|
||||
}
|
||||
|
||||
static inline uint16_t GET_16BIT_LSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint16_t)p[0] ) | ((uint16_t)p[1] << 8));
|
||||
}
|
||||
|
||||
static inline void PUT_16BIT_LSB_FIRST(void *vp, uint16_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (uint8_t)(value);
|
||||
p[1] = (uint8_t)(value >> 8);
|
||||
}
|
||||
|
||||
static inline uint64_t GET_64BIT_MSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint64_t)p[7] ) | ((uint64_t)p[6] << 8) |
|
||||
((uint64_t)p[5] << 16) | ((uint64_t)p[4] << 24) |
|
||||
((uint64_t)p[3] << 32) | ((uint64_t)p[2] << 40) |
|
||||
((uint64_t)p[1] << 48) | ((uint64_t)p[0] << 56));
|
||||
}
|
||||
|
||||
static inline void PUT_64BIT_MSB_FIRST(void *vp, uint64_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[7] = (uint8_t)(value);
|
||||
p[6] = (uint8_t)(value >> 8);
|
||||
p[5] = (uint8_t)(value >> 16);
|
||||
p[4] = (uint8_t)(value >> 24);
|
||||
p[3] = (uint8_t)(value >> 32);
|
||||
p[2] = (uint8_t)(value >> 40);
|
||||
p[1] = (uint8_t)(value >> 48);
|
||||
p[0] = (uint8_t)(value >> 56);
|
||||
}
|
||||
|
||||
static inline uint32_t GET_32BIT_MSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint32_t)p[3] ) | ((uint32_t)p[2] << 8) |
|
||||
((uint32_t)p[1] << 16) | ((uint32_t)p[0] << 24));
|
||||
}
|
||||
|
||||
static inline void PUT_32BIT_MSB_FIRST(void *vp, uint32_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[3] = (uint8_t)(value);
|
||||
p[2] = (uint8_t)(value >> 8);
|
||||
p[1] = (uint8_t)(value >> 16);
|
||||
p[0] = (uint8_t)(value >> 24);
|
||||
}
|
||||
|
||||
static inline uint16_t GET_16BIT_MSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint16_t)p[1] ) | ((uint16_t)p[0] << 8));
|
||||
}
|
||||
|
||||
static inline void PUT_16BIT_MSB_FIRST(void *vp, uint16_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[1] = (uint8_t)(value);
|
||||
p[0] = (uint8_t)(value >> 8);
|
||||
}
|
||||
|
||||
/* Replace NULL with the empty string, permitting an idiom in which we
|
||||
* get a string (pointer,length) pair that might be NULL,0 and can
|
||||
* then safely say things like printf("%.*s", length, NULLTOEMPTY(ptr)) */
|
||||
static inline const char *NULLTOEMPTY(const char *s)
|
||||
{
|
||||
return s ? s : "";
|
||||
}
|
||||
|
||||
/* StripCtrlChars, defined in stripctrl.c: an adapter you can put on
|
||||
* the front of one BinarySink and which functions as one in turn.
|
||||
* Interprets its input as a stream of multibyte characters in the
|
||||
* system locale, and removes any that are not either printable
|
||||
* characters or newlines. */
|
||||
struct StripCtrlChars {
|
||||
BinarySink_IMPLEMENTATION;
|
||||
/* and this is contained in a larger structure */
|
||||
};
|
||||
StripCtrlChars *stripctrl_new(
|
||||
BinarySink *bs_out, bool permit_cr, wchar_t substitution);
|
||||
StripCtrlChars *stripctrl_new_term_fn(
|
||||
BinarySink *bs_out, bool permit_cr, wchar_t substitution,
|
||||
Terminal *term, unsigned long (*translate)(
|
||||
Terminal *, term_utf8_decode *, unsigned char));
|
||||
#define stripctrl_new_term(bs, cr, sub, term) \
|
||||
stripctrl_new_term_fn(bs, cr, sub, term, term_translate)
|
||||
void stripctrl_retarget(StripCtrlChars *sccpub, BinarySink *new_bs_out);
|
||||
void stripctrl_reset(StripCtrlChars *sccpub);
|
||||
void stripctrl_free(StripCtrlChars *sanpub);
|
||||
void stripctrl_enable_line_limiting(StripCtrlChars *sccpub);
|
||||
char *stripctrl_string_ptrlen(StripCtrlChars *sccpub, ptrlen str);
|
||||
static inline char *stripctrl_string(StripCtrlChars *sccpub, const char *str)
|
||||
{
|
||||
return stripctrl_string_ptrlen(sccpub, ptrlen_from_asciz(str));
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Header for misc.c.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_MISC_H
|
||||
#define PUTTY_MISC_H
|
||||
|
||||
#include "defs.h"
|
||||
#include "puttymem.h"
|
||||
#include "marshal.h"
|
||||
|
||||
#include <stdio.h> /* for FILE * */
|
||||
#include <stdarg.h> /* for va_list */
|
||||
#include <stdlib.h> /* for abort */
|
||||
#include <time.h> /* for struct tm */
|
||||
#include <limits.h> /* for INT_MAX/MIN */
|
||||
#include <assert.h> /* for assert (obviously) */
|
||||
|
||||
unsigned long parse_blocksize(const char *bs);
|
||||
char ctrlparse(char *s, char **next);
|
||||
|
||||
size_t host_strcspn(const char *s, const char *set);
|
||||
char *host_strchr(const char *s, int c);
|
||||
char *host_strrchr(const char *s, int c);
|
||||
char *host_strduptrim(const char *s);
|
||||
|
||||
char *dupstr(const char *s);
|
||||
char *dupcat_fn(const char *s1, ...);
|
||||
#define dupcat(...) dupcat_fn(__VA_ARGS__, (const char *)NULL)
|
||||
char *dupprintf(const char *fmt, ...) PRINTF_LIKE(1, 2);
|
||||
char *dupvprintf(const char *fmt, va_list ap);
|
||||
void burnstr(char *string);
|
||||
|
||||
/*
|
||||
* The visible part of a strbuf structure. There's a surrounding
|
||||
* implementation struct in misc.c, which isn't exposed to client
|
||||
* code.
|
||||
*/
|
||||
struct strbuf {
|
||||
char *s;
|
||||
unsigned char *u;
|
||||
size_t len;
|
||||
BinarySink_IMPLEMENTATION;
|
||||
};
|
||||
|
||||
/* strbuf constructors: strbuf_new_nm and strbuf_new differ in that a
|
||||
* strbuf constructed using the _nm version will resize itself by
|
||||
* alloc/copy/smemclr/free instead of realloc. Use that version for
|
||||
* data sensitive enough that it's worth costing performance to
|
||||
* avoid copies of it lingering in process memory. */
|
||||
strbuf *strbuf_new(void);
|
||||
strbuf *strbuf_new_nm(void);
|
||||
|
||||
void strbuf_free(strbuf *buf);
|
||||
void *strbuf_append(strbuf *buf, size_t len);
|
||||
void strbuf_shrink_to(strbuf *buf, size_t new_len);
|
||||
void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove);
|
||||
char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */
|
||||
void strbuf_catf(strbuf *buf, const char *fmt, ...) PRINTF_LIKE(2, 3);
|
||||
void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap);
|
||||
static inline void strbuf_clear(strbuf *buf) { strbuf_shrink_to(buf, 0); }
|
||||
bool strbuf_chomp(strbuf *buf, char char_to_remove);
|
||||
|
||||
strbuf *strbuf_new_for_agent_query(void);
|
||||
void strbuf_finalise_agent_query(strbuf *buf);
|
||||
|
||||
/* String-to-Unicode converters that auto-allocate the destination and
|
||||
* work around the rather deficient interface of mb_to_wc.
|
||||
*
|
||||
* These actually live in miscucs.c, not misc.c (the distinction being
|
||||
* that the former is only linked into tools that also have the main
|
||||
* Unicode support). */
|
||||
wchar_t *dup_mb_to_wc_c(int codepage, int flags, const char *string, int len);
|
||||
wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string);
|
||||
|
||||
static inline int toint(unsigned u)
|
||||
{
|
||||
/*
|
||||
* Convert an unsigned to an int, without running into the
|
||||
* undefined behaviour which happens by the strict C standard if
|
||||
* the value overflows. You'd hope that sensible compilers would
|
||||
* do the sensible thing in response to a cast, but actually I
|
||||
* don't trust modern compilers not to do silly things like
|
||||
* assuming that _obviously_ you wouldn't have caused an overflow
|
||||
* and so they can elide an 'if (i < 0)' test immediately after
|
||||
* the cast.
|
||||
*
|
||||
* Sensible compilers ought of course to optimise this entire
|
||||
* function into 'just return the input value', and since it's
|
||||
* also declared inline, elide it completely in their output.
|
||||
*/
|
||||
if (u <= (unsigned)INT_MAX)
|
||||
return (int)u;
|
||||
else if (u >= (unsigned)INT_MIN) /* wrap in cast _to_ unsigned is OK */
|
||||
return INT_MIN + (int)(u - (unsigned)INT_MIN);
|
||||
else
|
||||
return INT_MIN; /* fallback; should never occur on binary machines */
|
||||
}
|
||||
|
||||
char *fgetline(FILE *fp);
|
||||
bool read_file_into(BinarySink *bs, FILE *fp);
|
||||
char *chomp(char *str);
|
||||
bool strstartswith(const char *s, const char *t);
|
||||
bool strendswith(const char *s, const char *t);
|
||||
|
||||
void base64_encode_atom(const unsigned char *data, int n, char *out);
|
||||
int base64_decode_atom(const char *atom, unsigned char *out);
|
||||
|
||||
struct bufchain_granule;
|
||||
struct bufchain_tag {
|
||||
struct bufchain_granule *head, *tail;
|
||||
size_t buffersize; /* current amount of buffered data */
|
||||
|
||||
void (*queue_idempotent_callback)(IdempotentCallback *ic);
|
||||
IdempotentCallback *ic;
|
||||
};
|
||||
|
||||
void bufchain_init(bufchain *ch);
|
||||
void bufchain_clear(bufchain *ch);
|
||||
size_t bufchain_size(bufchain *ch);
|
||||
void bufchain_add(bufchain *ch, const void *data, size_t len);
|
||||
ptrlen bufchain_prefix(bufchain *ch);
|
||||
void bufchain_consume(bufchain *ch, size_t len);
|
||||
void bufchain_fetch(bufchain *ch, void *data, size_t len);
|
||||
void bufchain_fetch_consume(bufchain *ch, void *data, size_t len);
|
||||
bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len);
|
||||
size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len);
|
||||
void bufchain_set_callback_inner(
|
||||
bufchain *ch, IdempotentCallback *ic,
|
||||
void (*queue_idempotent_callback)(IdempotentCallback *ic));
|
||||
static inline void bufchain_set_callback(bufchain *ch, IdempotentCallback *ic)
|
||||
{
|
||||
extern void queue_idempotent_callback(struct IdempotentCallback *ic);
|
||||
/* Wrapper that puts in the standard queue_idempotent_callback
|
||||
* function. Lives here rather than in utils.c so that standalone
|
||||
* programs can use the bufchain facility without this optional
|
||||
* callback feature and not need to provide a stub of
|
||||
* queue_idempotent_callback. */
|
||||
bufchain_set_callback_inner(ch, ic, queue_idempotent_callback);
|
||||
}
|
||||
|
||||
bool validate_manual_hostkey(char *key);
|
||||
|
||||
struct tm ltime(void);
|
||||
|
||||
/*
|
||||
* Special form of strcmp which can cope with NULL inputs. NULL is
|
||||
* defined to sort before even the empty string.
|
||||
*/
|
||||
int nullstrcmp(const char *a, const char *b);
|
||||
|
||||
static inline ptrlen make_ptrlen(const void *ptr, size_t len)
|
||||
{
|
||||
ptrlen pl;
|
||||
pl.ptr = ptr;
|
||||
pl.len = len;
|
||||
return pl;
|
||||
}
|
||||
|
||||
static inline ptrlen ptrlen_from_asciz(const char *str)
|
||||
{
|
||||
return make_ptrlen(str, strlen(str));
|
||||
}
|
||||
|
||||
static inline ptrlen ptrlen_from_strbuf(strbuf *sb)
|
||||
{
|
||||
return make_ptrlen(sb->u, sb->len);
|
||||
}
|
||||
|
||||
bool ptrlen_eq_string(ptrlen pl, const char *str);
|
||||
bool ptrlen_eq_ptrlen(ptrlen pl1, ptrlen pl2);
|
||||
int ptrlen_strcmp(ptrlen pl1, ptrlen pl2);
|
||||
/* ptrlen_startswith and ptrlen_endswith write through their 'tail'
|
||||
* argument if and only if it is non-NULL and they return true. Hence
|
||||
* you can write ptrlen_startswith(thing, prefix, &thing), writing
|
||||
* back to the same ptrlen it read from, to remove a prefix if present
|
||||
* and say whether it did so. */
|
||||
bool ptrlen_startswith(ptrlen whole, ptrlen prefix, ptrlen *tail);
|
||||
bool ptrlen_endswith(ptrlen whole, ptrlen suffix, ptrlen *tail);
|
||||
ptrlen ptrlen_get_word(ptrlen *input, const char *separators);
|
||||
char *mkstr(ptrlen pl);
|
||||
int string_length_for_printf(size_t);
|
||||
/* Derive two printf arguments from a ptrlen, suitable for "%.*s" */
|
||||
#define PTRLEN_PRINTF(pl) \
|
||||
string_length_for_printf((pl).len), (const char *)(pl).ptr
|
||||
/* Make a ptrlen out of a compile-time string literal. We try to
|
||||
* enforce that it _is_ a string literal by token-pasting "" on to it,
|
||||
* which should provoke a compile error if it's any other kind of
|
||||
* string. */
|
||||
#define PTRLEN_LITERAL(stringlit) \
|
||||
TYPECHECK("" stringlit "", make_ptrlen(stringlit, sizeof(stringlit)-1))
|
||||
/* Make a ptrlen out of a constant byte array. */
|
||||
#define PTRLEN_FROM_CONST_BYTES(a) make_ptrlen(a, sizeof(a))
|
||||
|
||||
/* Wipe sensitive data out of memory that's about to be freed. Simpler
|
||||
* than memset because we don't need the fill char parameter; also
|
||||
* attempts (by fiddly use of volatile) to inhibit the compiler from
|
||||
* over-cleverly trying to optimise the memset away because it knows
|
||||
* the variable is going out of scope. */
|
||||
void smemclr(void *b, size_t len);
|
||||
|
||||
/* Compare two fixed-length chunks of memory for equality, without
|
||||
* data-dependent control flow (so an attacker with a very accurate
|
||||
* stopwatch can't try to guess where the first mismatching byte was).
|
||||
* Returns false for mismatch or true for equality (unlike memcmp),
|
||||
* hinted at by the 'eq' in the name. */
|
||||
bool smemeq(const void *av, const void *bv, size_t len);
|
||||
|
||||
/* Encode a single UTF-8 character. Assumes that illegal characters
|
||||
* (such as things in the surrogate range, or > 0x10FFFF) have already
|
||||
* been removed. */
|
||||
size_t encode_utf8(void *output, unsigned long ch);
|
||||
|
||||
char *buildinfo(const char *newline);
|
||||
|
||||
/*
|
||||
* A function you can put at points in the code where execution should
|
||||
* never reach in the first place. Better than assert(false), or even
|
||||
* assert(false && "some explanatory message"), because some compilers
|
||||
* don't interpret assert(false) as a declaration of unreachability,
|
||||
* so they may still warn about pointless things like some variable
|
||||
* not being initialised on the unreachable code path.
|
||||
*
|
||||
* I follow the assertion with a call to abort() just in case someone
|
||||
* compiles with -DNDEBUG, and I wrap that abort inside my own
|
||||
* function labelled NORETURN just in case some unusual kind of system
|
||||
* header wasn't foresighted enough to label abort() itself that way.
|
||||
*/
|
||||
static inline NORETURN void unreachable_internal(void) { abort(); }
|
||||
#define unreachable(msg) (assert(false && msg), unreachable_internal())
|
||||
|
||||
/*
|
||||
* Debugging functions.
|
||||
*
|
||||
* Output goes to debug.log
|
||||
*
|
||||
* debug() is like printf().
|
||||
*
|
||||
* dmemdump() and dmemdumpl() both do memory dumps. The difference
|
||||
* is that dmemdumpl() is more suited for when the memory address is
|
||||
* important (say because you'll be recording pointer values later
|
||||
* on). dmemdump() is more concise.
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
void debug_printf(const char *fmt, ...) PRINTF_LIKE(1, 2);
|
||||
void debug_memdump(const void *buf, int len, bool L);
|
||||
#define debug(...) (debug_printf(__VA_ARGS__))
|
||||
#define dmemdump(buf,len) (debug_memdump(buf, len, false))
|
||||
#define dmemdumpl(buf,len) (debug_memdump(buf, len, true))
|
||||
#else
|
||||
#define debug(...) ((void)0)
|
||||
#define dmemdump(buf,len) ((void)0)
|
||||
#define dmemdumpl(buf,len) ((void)0)
|
||||
#endif
|
||||
|
||||
#ifndef lenof
|
||||
#define lenof(x) ( (sizeof((x))) / (sizeof(*(x))))
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
#define min(x,y) ( (x) < (y) ? (x) : (y) )
|
||||
#endif
|
||||
#ifndef max
|
||||
#define max(x,y) ( (x) > (y) ? (x) : (y) )
|
||||
#endif
|
||||
|
||||
static inline uint64_t GET_64BIT_LSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint64_t)p[0] ) | ((uint64_t)p[1] << 8) |
|
||||
((uint64_t)p[2] << 16) | ((uint64_t)p[3] << 24) |
|
||||
((uint64_t)p[4] << 32) | ((uint64_t)p[5] << 40) |
|
||||
((uint64_t)p[6] << 48) | ((uint64_t)p[7] << 56));
|
||||
}
|
||||
|
||||
static inline void PUT_64BIT_LSB_FIRST(void *vp, uint64_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (uint8_t)(value);
|
||||
p[1] = (uint8_t)(value >> 8);
|
||||
p[2] = (uint8_t)(value >> 16);
|
||||
p[3] = (uint8_t)(value >> 24);
|
||||
p[4] = (uint8_t)(value >> 32);
|
||||
p[5] = (uint8_t)(value >> 40);
|
||||
p[6] = (uint8_t)(value >> 48);
|
||||
p[7] = (uint8_t)(value >> 56);
|
||||
}
|
||||
|
||||
static inline uint32_t GET_32BIT_LSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint32_t)p[0] ) | ((uint32_t)p[1] << 8) |
|
||||
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24));
|
||||
}
|
||||
|
||||
static inline void PUT_32BIT_LSB_FIRST(void *vp, uint32_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (uint8_t)(value);
|
||||
p[1] = (uint8_t)(value >> 8);
|
||||
p[2] = (uint8_t)(value >> 16);
|
||||
p[3] = (uint8_t)(value >> 24);
|
||||
}
|
||||
|
||||
static inline uint16_t GET_16BIT_LSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint16_t)p[0] ) | ((uint16_t)p[1] << 8));
|
||||
}
|
||||
|
||||
static inline void PUT_16BIT_LSB_FIRST(void *vp, uint16_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (uint8_t)(value);
|
||||
p[1] = (uint8_t)(value >> 8);
|
||||
}
|
||||
|
||||
static inline uint64_t GET_64BIT_MSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint64_t)p[7] ) | ((uint64_t)p[6] << 8) |
|
||||
((uint64_t)p[5] << 16) | ((uint64_t)p[4] << 24) |
|
||||
((uint64_t)p[3] << 32) | ((uint64_t)p[2] << 40) |
|
||||
((uint64_t)p[1] << 48) | ((uint64_t)p[0] << 56));
|
||||
}
|
||||
|
||||
static inline void PUT_64BIT_MSB_FIRST(void *vp, uint64_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[7] = (uint8_t)(value);
|
||||
p[6] = (uint8_t)(value >> 8);
|
||||
p[5] = (uint8_t)(value >> 16);
|
||||
p[4] = (uint8_t)(value >> 24);
|
||||
p[3] = (uint8_t)(value >> 32);
|
||||
p[2] = (uint8_t)(value >> 40);
|
||||
p[1] = (uint8_t)(value >> 48);
|
||||
p[0] = (uint8_t)(value >> 56);
|
||||
}
|
||||
|
||||
static inline uint32_t GET_32BIT_MSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint32_t)p[3] ) | ((uint32_t)p[2] << 8) |
|
||||
((uint32_t)p[1] << 16) | ((uint32_t)p[0] << 24));
|
||||
}
|
||||
|
||||
static inline void PUT_32BIT_MSB_FIRST(void *vp, uint32_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[3] = (uint8_t)(value);
|
||||
p[2] = (uint8_t)(value >> 8);
|
||||
p[1] = (uint8_t)(value >> 16);
|
||||
p[0] = (uint8_t)(value >> 24);
|
||||
}
|
||||
|
||||
static inline uint16_t GET_16BIT_MSB_FIRST(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (((uint16_t)p[1] ) | ((uint16_t)p[0] << 8));
|
||||
}
|
||||
|
||||
static inline void PUT_16BIT_MSB_FIRST(void *vp, uint16_t value)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[1] = (uint8_t)(value);
|
||||
p[0] = (uint8_t)(value >> 8);
|
||||
}
|
||||
|
||||
/* Replace NULL with the empty string, permitting an idiom in which we
|
||||
* get a string (pointer,length) pair that might be NULL,0 and can
|
||||
* then safely say things like printf("%.*s", length, NULLTOEMPTY(ptr)) */
|
||||
static inline const char *NULLTOEMPTY(const char *s)
|
||||
{
|
||||
return s ? s : "";
|
||||
}
|
||||
|
||||
/* StripCtrlChars, defined in stripctrl.c: an adapter you can put on
|
||||
* the front of one BinarySink and which functions as one in turn.
|
||||
* Interprets its input as a stream of multibyte characters in the
|
||||
* system locale, and removes any that are not either printable
|
||||
* characters or newlines. */
|
||||
struct StripCtrlChars {
|
||||
BinarySink_IMPLEMENTATION;
|
||||
/* and this is contained in a larger structure */
|
||||
};
|
||||
StripCtrlChars *stripctrl_new(
|
||||
BinarySink *bs_out, bool permit_cr, wchar_t substitution);
|
||||
StripCtrlChars *stripctrl_new_term_fn(
|
||||
BinarySink *bs_out, bool permit_cr, wchar_t substitution,
|
||||
Terminal *term, unsigned long (*translate)(
|
||||
Terminal *, term_utf8_decode *, unsigned char));
|
||||
#define stripctrl_new_term(bs, cr, sub, term) \
|
||||
stripctrl_new_term_fn(bs, cr, sub, term, term_translate)
|
||||
void stripctrl_retarget(StripCtrlChars *sccpub, BinarySink *new_bs_out);
|
||||
void stripctrl_reset(StripCtrlChars *sccpub);
|
||||
void stripctrl_free(StripCtrlChars *sanpub);
|
||||
void stripctrl_enable_line_limiting(StripCtrlChars *sccpub);
|
||||
char *stripctrl_string_ptrlen(StripCtrlChars *sccpub, ptrlen str);
|
||||
static inline char *stripctrl_string(StripCtrlChars *sccpub, const char *str)
|
||||
{
|
||||
return stripctrl_string_ptrlen(sccpub, ptrlen_from_asciz(str));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -211,13 +211,8 @@ void pageant_make_keylist2(BinarySink *bs)
|
||||
}
|
||||
}
|
||||
|
||||
static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((format (PUTTY_PRINTF_ARCHETYPE, 3, 4)))
|
||||
#endif
|
||||
;
|
||||
|
||||
static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
|
||||
static PRINTF_LIKE(3, 4) void plog(void *logctx, pageant_logfn_t logfn,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
/*
|
||||
* This is the wrapper that takes a variadic argument list and
|
||||
@@ -481,19 +476,7 @@ void pageant_handle_msg(BinarySink *bs,
|
||||
|
||||
plog(logctx, logfn, "request: SSH1_AGENTC_ADD_RSA_IDENTITY");
|
||||
|
||||
key = snew(RSAKey);
|
||||
memset(key, 0, sizeof(RSAKey));
|
||||
|
||||
get_rsa_ssh1_pub(msg, key, RSA_SSH1_MODULUS_FIRST);
|
||||
get_rsa_ssh1_priv(msg, key);
|
||||
|
||||
/* SSH-1 names p and q the other way round, i.e. we have
|
||||
* the inverse of p mod q and not of q mod p. We swap the
|
||||
* names, because our internal RSA wants iqmp. */
|
||||
key->iqmp = get_mp_ssh1(msg);
|
||||
key->q = get_mp_ssh1(msg);
|
||||
key->p = get_mp_ssh1(msg);
|
||||
|
||||
key = get_rsa_ssh1_priv_agent(msg);
|
||||
key->comment = mkstr(get_string(msg));
|
||||
|
||||
if (get_err(msg)) {
|
||||
@@ -1166,11 +1149,7 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase,
|
||||
* length, so add a placeholder here to fill in
|
||||
* afterwards */
|
||||
put_uint32(blob, 0);
|
||||
#ifdef MOD_WINCRYPT
|
||||
if (!ssh2_userkey_loadpub((const Filename **)&filename, NULL, BinarySink_UPCAST(blob),
|
||||
#else
|
||||
if (!ssh2_userkey_loadpub(filename, NULL, BinarySink_UPCAST(blob),
|
||||
#endif
|
||||
NULL, &error)) {
|
||||
*retstr = dupprintf("Couldn't load private key (%s)", error);
|
||||
strbuf_free(blob);
|
||||
@@ -1418,10 +1397,14 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase,
|
||||
if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) {
|
||||
*retstr = dupstr("The already running Pageant "
|
||||
"refused to add the key.");
|
||||
sfree(skey->comment);
|
||||
ssh_key_free(skey->key);
|
||||
sfree(skey);
|
||||
sfree(response);
|
||||
return PAGEANT_ACTION_FAILURE;
|
||||
}
|
||||
|
||||
sfree(skey->comment);
|
||||
ssh_key_free(skey->key);
|
||||
sfree(skey);
|
||||
sfree(response);
|
||||
@@ -1519,6 +1502,7 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
|
||||
callback(callback_ctx, fingerprint, cbkey.comment, &cbkey);
|
||||
sfree(fingerprint);
|
||||
sfree(cbkey.comment);
|
||||
strbuf_free(cbkey.blob);
|
||||
}
|
||||
|
||||
sfree(keylist);
|
||||
@@ -786,6 +786,45 @@ void portfwdmgr_free(PortFwdManager *mgr)
|
||||
sfree(mgr);
|
||||
}
|
||||
|
||||
#ifdef MOD_PERSO
|
||||
// Manage sftpconnect that ends with :* (create a local forward port dynamically)
|
||||
void AddDynamicSFTPConnect( Conf *conf ) {
|
||||
char * p = conf_get_str( conf, CONF_sftpconnect ) ;
|
||||
if( p[0]==':' ) {
|
||||
if( (conf_get_str( conf, CONF_username )!=NULL) && (strlen(conf_get_str( conf, CONF_username ))>0) ) {
|
||||
char *b ;
|
||||
b = (char*) malloc( strlen(conf_get_str( conf, CONF_username )) + 20 ) ;
|
||||
sprintf( b, "%s@localhost%s", conf_get_str( conf, CONF_username ),p ) ;
|
||||
conf_set_str( conf, CONF_sftpconnect, b ) ;
|
||||
p = conf_get_str( conf, CONF_sftpconnect ) ;
|
||||
free(b);
|
||||
} else if( strstr(conf_get_str( conf, CONF_host ),"@")!=NULL ) {
|
||||
char *b ;
|
||||
b = (char*) malloc( strlen(conf_get_str( conf, CONF_host )) + 20 ) ;
|
||||
strcpy(b,conf_get_str( conf, CONF_host ));
|
||||
strstr(b,"@")[0]='\0';
|
||||
strcat(b,"@localhost");
|
||||
strcat(b,p);
|
||||
conf_set_str( conf, CONF_sftpconnect, b ) ;
|
||||
p = conf_get_str( conf, CONF_sftpconnect ) ;
|
||||
free(b) ;
|
||||
}
|
||||
}
|
||||
if( (p[strlen(p)-1]=='*') && (p[strlen(p)-2]==':') ) {
|
||||
char key[10], val[20] ;
|
||||
p[strlen(p)-2]='\0' ;
|
||||
int rnd = (rand() % (65000 - 60000)) + 60000 ;
|
||||
sprintf(key,"L%d",rnd);
|
||||
sprintf(val,"localhost:%d",conf_get_int(conf, CONF_port));
|
||||
conf_set_str_str(conf, CONF_portfwd, key, val) ;
|
||||
char *b = (char*) malloc( strlen(p)+20 );
|
||||
sprintf(b,"%s:%d",p,rnd) ;
|
||||
conf_set_str( conf, CONF_sftpconnect, b ) ;
|
||||
free(b);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
|
||||
{
|
||||
PortFwdRecord *pfr;
|
||||
@@ -805,7 +844,6 @@ void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
|
||||
*/
|
||||
for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++)
|
||||
pfr->status = DESTROY;
|
||||
|
||||
for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
|
||||
val != NULL;
|
||||
val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,111 +1,111 @@
|
||||
/*
|
||||
* Network proxy abstraction in PuTTY
|
||||
*
|
||||
* A proxy layer, if necessary, wedges itself between the
|
||||
* network code and the higher level backend.
|
||||
*
|
||||
* Supported proxies: HTTP CONNECT, generic telnet, SOCKS 4 & 5
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_PROXY_H
|
||||
#define PUTTY_PROXY_H
|
||||
|
||||
#define PROXY_ERROR_GENERAL 8000
|
||||
#define PROXY_ERROR_UNEXPECTED 8001
|
||||
|
||||
typedef struct ProxySocket ProxySocket;
|
||||
|
||||
struct ProxySocket {
|
||||
const char *error;
|
||||
|
||||
Socket *sub_socket;
|
||||
Plug *plug;
|
||||
SockAddr *remote_addr;
|
||||
int remote_port;
|
||||
|
||||
bufchain pending_output_data;
|
||||
bufchain pending_oob_output_data;
|
||||
bufchain pending_input_data;
|
||||
bool pending_eof;
|
||||
|
||||
#define PROXY_STATE_NEW -1
|
||||
#define PROXY_STATE_ACTIVE 0
|
||||
|
||||
int state; /* proxy states greater than 0 are implementation
|
||||
* dependent, but represent various stages/states
|
||||
* of the initialization/setup/negotiation with the
|
||||
* proxy server.
|
||||
*/
|
||||
bool freeze; /* should we freeze the underlying socket when
|
||||
* we are done with the proxy negotiation? this
|
||||
* simply caches the value of sk_set_frozen calls.
|
||||
*/
|
||||
|
||||
#define PROXY_CHANGE_NEW -1
|
||||
#define PROXY_CHANGE_CLOSING 0
|
||||
#define PROXY_CHANGE_SENT 1
|
||||
#define PROXY_CHANGE_RECEIVE 2
|
||||
#define PROXY_CHANGE_ACCEPTING 3
|
||||
|
||||
/* something has changed (a call from the sub socket
|
||||
* layer into our Proxy Plug layer, or we were just
|
||||
* created, etc), so the proxy layer needs to handle
|
||||
* this change (the type of which is the second argument)
|
||||
* and further the proxy negotiation process.
|
||||
*/
|
||||
|
||||
int (*negotiate) (ProxySocket * /* this */, int /* change type */);
|
||||
|
||||
/* current arguments of plug handlers
|
||||
* (for use by proxy's negotiate function)
|
||||
*/
|
||||
|
||||
/* closing */
|
||||
const char *closing_error_msg;
|
||||
int closing_error_code;
|
||||
bool closing_calling_back;
|
||||
|
||||
/* receive */
|
||||
bool receive_urgent;
|
||||
const char *receive_data;
|
||||
int receive_len;
|
||||
|
||||
/* accepting */
|
||||
accept_fn_t accepting_constructor;
|
||||
accept_ctx_t accepting_ctx;
|
||||
|
||||
/* configuration, used to look up proxy settings */
|
||||
Conf *conf;
|
||||
|
||||
/* CHAP transient data */
|
||||
int chap_num_attributes;
|
||||
int chap_num_attributes_processed;
|
||||
int chap_current_attribute;
|
||||
int chap_current_datalen;
|
||||
|
||||
Socket sock;
|
||||
Plug plugimpl;
|
||||
};
|
||||
|
||||
extern void proxy_activate (ProxySocket *);
|
||||
|
||||
extern int proxy_http_negotiate (ProxySocket *, int);
|
||||
extern int proxy_telnet_negotiate (ProxySocket *, int);
|
||||
extern int proxy_socks4_negotiate (ProxySocket *, int);
|
||||
extern int proxy_socks5_negotiate (ProxySocket *, int);
|
||||
|
||||
/*
|
||||
* This may be reused by local-command proxies on individual
|
||||
* platforms.
|
||||
*/
|
||||
char *format_telnet_command(SockAddr *addr, int port, Conf *conf);
|
||||
|
||||
/*
|
||||
* These are implemented in cproxy.c or nocproxy.c, depending on
|
||||
* whether encrypted proxy authentication is available.
|
||||
*/
|
||||
extern void proxy_socks5_offerencryptedauth(BinarySink *);
|
||||
extern int proxy_socks5_handlechap (ProxySocket *);
|
||||
extern int proxy_socks5_selectchap(ProxySocket *);
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Network proxy abstraction in PuTTY
|
||||
*
|
||||
* A proxy layer, if necessary, wedges itself between the
|
||||
* network code and the higher level backend.
|
||||
*
|
||||
* Supported proxies: HTTP CONNECT, generic telnet, SOCKS 4 & 5
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_PROXY_H
|
||||
#define PUTTY_PROXY_H
|
||||
|
||||
#define PROXY_ERROR_GENERAL 8000
|
||||
#define PROXY_ERROR_UNEXPECTED 8001
|
||||
|
||||
typedef struct ProxySocket ProxySocket;
|
||||
|
||||
struct ProxySocket {
|
||||
const char *error;
|
||||
|
||||
Socket *sub_socket;
|
||||
Plug *plug;
|
||||
SockAddr *remote_addr;
|
||||
int remote_port;
|
||||
|
||||
bufchain pending_output_data;
|
||||
bufchain pending_oob_output_data;
|
||||
bufchain pending_input_data;
|
||||
bool pending_eof;
|
||||
|
||||
#define PROXY_STATE_NEW -1
|
||||
#define PROXY_STATE_ACTIVE 0
|
||||
|
||||
int state; /* proxy states greater than 0 are implementation
|
||||
* dependent, but represent various stages/states
|
||||
* of the initialization/setup/negotiation with the
|
||||
* proxy server.
|
||||
*/
|
||||
bool freeze; /* should we freeze the underlying socket when
|
||||
* we are done with the proxy negotiation? this
|
||||
* simply caches the value of sk_set_frozen calls.
|
||||
*/
|
||||
|
||||
#define PROXY_CHANGE_NEW -1
|
||||
#define PROXY_CHANGE_CLOSING 0
|
||||
#define PROXY_CHANGE_SENT 1
|
||||
#define PROXY_CHANGE_RECEIVE 2
|
||||
#define PROXY_CHANGE_ACCEPTING 3
|
||||
|
||||
/* something has changed (a call from the sub socket
|
||||
* layer into our Proxy Plug layer, or we were just
|
||||
* created, etc), so the proxy layer needs to handle
|
||||
* this change (the type of which is the second argument)
|
||||
* and further the proxy negotiation process.
|
||||
*/
|
||||
|
||||
int (*negotiate) (ProxySocket * /* this */, int /* change type */);
|
||||
|
||||
/* current arguments of plug handlers
|
||||
* (for use by proxy's negotiate function)
|
||||
*/
|
||||
|
||||
/* closing */
|
||||
const char *closing_error_msg;
|
||||
int closing_error_code;
|
||||
bool closing_calling_back;
|
||||
|
||||
/* receive */
|
||||
bool receive_urgent;
|
||||
const char *receive_data;
|
||||
int receive_len;
|
||||
|
||||
/* accepting */
|
||||
accept_fn_t accepting_constructor;
|
||||
accept_ctx_t accepting_ctx;
|
||||
|
||||
/* configuration, used to look up proxy settings */
|
||||
Conf *conf;
|
||||
|
||||
/* CHAP transient data */
|
||||
int chap_num_attributes;
|
||||
int chap_num_attributes_processed;
|
||||
int chap_current_attribute;
|
||||
int chap_current_datalen;
|
||||
|
||||
Socket sock;
|
||||
Plug plugimpl;
|
||||
};
|
||||
|
||||
extern void proxy_activate (ProxySocket *);
|
||||
|
||||
extern int proxy_http_negotiate (ProxySocket *, int);
|
||||
extern int proxy_telnet_negotiate (ProxySocket *, int);
|
||||
extern int proxy_socks4_negotiate (ProxySocket *, int);
|
||||
extern int proxy_socks5_negotiate (ProxySocket *, int);
|
||||
|
||||
/*
|
||||
* This may be reused by local-command proxies on individual
|
||||
* platforms.
|
||||
*/
|
||||
char *format_telnet_command(SockAddr *addr, int port, Conf *conf);
|
||||
|
||||
/*
|
||||
* These are implemented in cproxy.c or nocproxy.c, depending on
|
||||
* whether encrypted proxy authentication is available.
|
||||
*/
|
||||
extern void proxy_socks5_offerencryptedauth(BinarySink *);
|
||||
extern int proxy_socks5_handlechap (ProxySocket *);
|
||||
extern int proxy_socks5_selectchap(ProxySocket *);
|
||||
|
||||
#endif
|
||||
@@ -52,7 +52,7 @@ static void sink(const char *targ, const char *src);
|
||||
|
||||
#ifdef MOD_PERSO
|
||||
void SetAutoStoreSSHKey( void ) ;
|
||||
#if (defined MOD_PERSO) && (!defined FDJ)
|
||||
#if (defined MOD_PERSO) && (!defined FLJ)
|
||||
char *appname = "PSCP";
|
||||
#else
|
||||
const char *const appname = "PSCP";
|
||||
@@ -123,14 +123,14 @@ static void abandon_stats(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void tell_user(FILE *stream, const char *fmt, ...)
|
||||
static PRINTF_LIKE(2, 3) void tell_user(FILE *stream, const char *fmt, ...)
|
||||
{
|
||||
char *str, *str2;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
str = dupvprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
str2 = dupcat(str, "\n", NULL);
|
||||
str2 = dupcat(str, "\n");
|
||||
sfree(str);
|
||||
abandon_stats();
|
||||
tell_str(stream, str2);
|
||||
@@ -234,14 +234,14 @@ static void ssh_scp_init(void)
|
||||
/*
|
||||
* Print an error message and exit after closing the SSH link.
|
||||
*/
|
||||
static NORETURN void bump(const char *fmt, ...)
|
||||
static NORETURN PRINTF_LIKE(1, 2) void bump(const char *fmt, ...)
|
||||
{
|
||||
char *str, *str2;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
str = dupvprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
str2 = dupcat(str, "\n", NULL);
|
||||
str2 = dupcat(str, "\n");
|
||||
sfree(str);
|
||||
abandon_stats();
|
||||
tell_str(stderr, str2);
|
||||
@@ -775,7 +775,7 @@ int scp_send_filename(const char *name, uint64_t size, int permissions)
|
||||
struct fxp_attrs attrs;
|
||||
|
||||
if (scp_sftp_targetisdir) {
|
||||
fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);
|
||||
fullname = dupcat(scp_sftp_remotepath, "/", name);
|
||||
} else {
|
||||
fullname = dupstr(scp_sftp_remotepath);
|
||||
}
|
||||
@@ -825,6 +825,15 @@ int scp_send_filedata(char *data, int len)
|
||||
}
|
||||
|
||||
while (!xfer_upload_ready(scp_sftp_xfer)) {
|
||||
if (toplevel_callback_pending()) {
|
||||
/* If we have pending callbacks, they might make
|
||||
* xfer_upload_ready start to return true. So we should
|
||||
* run them and then re-check xfer_upload_ready, before
|
||||
* we go as far as waiting for an entire packet to
|
||||
* arrive. */
|
||||
run_toplevel_callbacks();
|
||||
continue;
|
||||
}
|
||||
pktin = sftp_recv();
|
||||
ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin);
|
||||
if (ret <= 0) {
|
||||
@@ -930,7 +939,7 @@ int scp_send_dirname(const char *name, int modes)
|
||||
bool ret;
|
||||
|
||||
if (scp_sftp_targetisdir) {
|
||||
fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);
|
||||
fullname = dupcat(scp_sftp_remotepath, "/", name);
|
||||
} else {
|
||||
fullname = dupstr(scp_sftp_remotepath);
|
||||
}
|
||||
@@ -1141,8 +1150,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
|
||||
if (head->namepos < head->namelen) {
|
||||
head->matched_something = true;
|
||||
fname = dupcat(head->dirpath, "/",
|
||||
head->names[head->namepos++].filename,
|
||||
NULL);
|
||||
head->names[head->namepos++].filename);
|
||||
must_free_fname = true;
|
||||
} else {
|
||||
/*
|
||||
@@ -1320,7 +1328,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
|
||||
act->action = SCP_SINK_RETRY;
|
||||
} else {
|
||||
act->action = SCP_SINK_DIR;
|
||||
act->buf->len = 0;
|
||||
strbuf_clear(act->buf);
|
||||
put_asciz(act->buf, stripslashes(fname, false));
|
||||
act->name = act->buf->s;
|
||||
act->size = 0; /* duhh, it's a directory */
|
||||
@@ -1340,7 +1348,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
|
||||
* It's a file. Return SCP_SINK_FILE.
|
||||
*/
|
||||
act->action = SCP_SINK_FILE;
|
||||
act->buf->len = 0;
|
||||
strbuf_clear(act->buf);
|
||||
put_asciz(act->buf, stripslashes(fname, false));
|
||||
act->name = act->buf->s;
|
||||
if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
|
||||
@@ -1368,7 +1376,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
|
||||
char ch;
|
||||
|
||||
act->settime = false;
|
||||
act->buf->len = 0;
|
||||
strbuf_clear(act->buf);
|
||||
|
||||
while (!done) {
|
||||
if (!ssh_scp_recv(&ch, 1))
|
||||
@@ -1401,7 +1409,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
|
||||
&act->mtime, &act->atime) == 2) {
|
||||
act->settime = true;
|
||||
backend_send(backend, "", 1);
|
||||
act->buf->len = 0;
|
||||
strbuf_clear(act->buf);
|
||||
continue; /* go round again */
|
||||
}
|
||||
bump("Protocol error: Illegal time format");
|
||||
@@ -1555,14 +1563,14 @@ int scp_finish_filerecv(void)
|
||||
* Send an error message to the other side and to the screen.
|
||||
* Increment error counter.
|
||||
*/
|
||||
static void run_err(const char *fmt, ...)
|
||||
static PRINTF_LIKE(1, 2) void run_err(const char *fmt, ...)
|
||||
{
|
||||
char *str, *str2;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
errs++;
|
||||
str = dupvprintf(fmt, ap);
|
||||
str2 = dupcat("pscp: ", str, "\n", NULL);
|
||||
str2 = dupcat("pscp: ", str, "\n");
|
||||
sfree(str);
|
||||
scp_send_errmsg(str2);
|
||||
abandon_stats();
|
||||
@@ -1710,7 +1718,7 @@ static void rsource(const char *src)
|
||||
if (dir != NULL) {
|
||||
char *filename;
|
||||
while ((filename = read_filename(dir)) != NULL) {
|
||||
char *foundfile = dupcat(src, "/", filename, NULL);
|
||||
char *foundfile = dupcat(src, "/", filename);
|
||||
source(foundfile);
|
||||
sfree(foundfile);
|
||||
sfree(filename);
|
||||
@@ -1803,7 +1811,7 @@ static void sink(const char *targ, const char *src)
|
||||
with_stripctrl(santarg, act.name) {
|
||||
tell_user(stderr, "warning: remote host sent a"
|
||||
" compound pathname '%s'", sanname);
|
||||
tell_user(stderr, " renaming local",
|
||||
tell_user(stderr, " renaming local"
|
||||
" file to '%s'", santarg);
|
||||
}
|
||||
}
|
||||
@@ -2249,7 +2257,7 @@ int psftp_main(int argc, char *argv[])
|
||||
int i;
|
||||
bool sanitise_stderr = true;
|
||||
|
||||
default_protocol = PROT_TELNET;
|
||||
default_protocol = PROT_SSH;
|
||||
|
||||
flags = 0
|
||||
#ifdef FLAG_SYNCAGENT
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#ifdef MOD_PERSO
|
||||
void SetAutoStoreSSHKey( void ) ;
|
||||
#if (defined MOD_PERSO) && (!defined FDJ)
|
||||
#if (defined MOD_PERSO) && (!defined FLJ)
|
||||
char *appname = "PSFTP";
|
||||
#else
|
||||
const char *const appname = "PSFTP";
|
||||
@@ -134,7 +134,7 @@ char *canonify(const char *name)
|
||||
slash = "";
|
||||
else
|
||||
slash = "/";
|
||||
fullname = dupcat(pwd, slash, name, NULL);
|
||||
fullname = dupcat(pwd, slash, name);
|
||||
}
|
||||
|
||||
req = fxp_realpath_send(fullname);
|
||||
@@ -213,8 +213,8 @@ char *canonify(const char *name)
|
||||
* component. Concatenate the last component and return.
|
||||
*/
|
||||
returnname = dupcat(canonname,
|
||||
canonname[strlen(canonname) - 1] ==
|
||||
'/' ? "" : "/", fullname + i + 1, NULL);
|
||||
(strendswith(canonname, "/") ? "" : "/"),
|
||||
fullname + i + 1);
|
||||
sfree(fullname);
|
||||
sfree(canonname);
|
||||
return returnname;
|
||||
@@ -386,7 +386,7 @@ bool sftp_get_file(char *fname, char *outfname, bool recurse, bool restart)
|
||||
char *nextfname, *nextoutfname;
|
||||
bool retd;
|
||||
|
||||
nextfname = dupcat(fname, "/", ournames[i]->filename, NULL);
|
||||
nextfname = dupcat(fname, "/", ournames[i]->filename);
|
||||
nextoutfname = dir_file_cat(outfname, ournames[i]->filename);
|
||||
retd = sftp_get_file(
|
||||
nextfname, nextoutfname, recurse, restart);
|
||||
@@ -611,7 +611,7 @@ bool sftp_put_file(char *fname, char *outfname, bool recurse, bool restart)
|
||||
if (restart) {
|
||||
while (i < nnames) {
|
||||
char *nextoutfname;
|
||||
nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
|
||||
nextoutfname = dupcat(outfname, "/", ournames[i]);
|
||||
req = fxp_stat_send(nextoutfname);
|
||||
pktin = sftp_wait_for_reply(req);
|
||||
result = fxp_stat_recv(pktin, req, &attrs);
|
||||
@@ -635,7 +635,7 @@ bool sftp_put_file(char *fname, char *outfname, bool recurse, bool restart)
|
||||
bool retd;
|
||||
|
||||
nextfname = dir_file_cat(fname, ournames[i]);
|
||||
nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
|
||||
nextoutfname = dupcat(outfname, "/", ournames[i]);
|
||||
retd = sftp_put_file(nextfname, nextoutfname, recurse, restart);
|
||||
restart = false; /* after first partial file, do full */
|
||||
sfree(nextoutfname);
|
||||
@@ -734,6 +734,16 @@ bool sftp_put_file(char *fname, char *outfname, bool recurse, bool restart)
|
||||
}
|
||||
}
|
||||
|
||||
if (toplevel_callback_pending() && !err && !eof) {
|
||||
/* If we have pending callbacks, they might make
|
||||
* xfer_upload_ready start to return true. So we should
|
||||
* run them and then re-check xfer_upload_ready, before
|
||||
* we go as far as waiting for an entire packet to
|
||||
* arrive. */
|
||||
run_toplevel_callbacks();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!xfer_done(xfer)) {
|
||||
pktin = sftp_recv();
|
||||
ret = xfer_upload_gotpkt(xfer, pktin);
|
||||
@@ -1566,7 +1576,7 @@ static bool sftp_action_mv(void *vctx, char *srcfname)
|
||||
|
||||
p = srcfname + strlen(srcfname);
|
||||
while (p > srcfname && p[-1] != '/') p--;
|
||||
newname = dupcat(ctx->dstfname, "/", p, NULL);
|
||||
newname = dupcat(ctx->dstfname, "/", p);
|
||||
newcanon = canonify(newname);
|
||||
sfree(newname);
|
||||
|
||||
@@ -2315,6 +2325,16 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static void sftp_cmd_free(struct sftp_command *cmd)
|
||||
{
|
||||
if (cmd->words) {
|
||||
for (size_t i = 0; i < cmd->nwords; i++)
|
||||
sfree(cmd->words[i]);
|
||||
sfree(cmd->words);
|
||||
}
|
||||
sfree(cmd);
|
||||
}
|
||||
|
||||
static int do_sftp_init(void)
|
||||
{
|
||||
struct sftp_packet *pktin;
|
||||
@@ -2389,13 +2409,7 @@ int do_sftp(int mode, int modeflags, char *batchfile)
|
||||
if (!cmd)
|
||||
break;
|
||||
ret = cmd->obey(cmd);
|
||||
if (cmd->words) {
|
||||
int i;
|
||||
for(i = 0; i < cmd->nwords; i++)
|
||||
sfree(cmd->words[i]);
|
||||
sfree(cmd->words);
|
||||
}
|
||||
sfree(cmd);
|
||||
sftp_cmd_free(cmd);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
@@ -2412,6 +2426,7 @@ int do_sftp(int mode, int modeflags, char *batchfile)
|
||||
if (!cmd)
|
||||
break;
|
||||
ret = cmd->obey(cmd);
|
||||
sftp_cmd_free(cmd);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (ret == 0) {
|
||||
@@ -31,6 +31,7 @@
|
||||
#define MAX_TICK_MINS (INT_MAX / (60 * TICKSPERSEC))
|
||||
|
||||
#ifdef MOD_PERSO
|
||||
extern int debug_flag ;
|
||||
#define ATTR_ITALIC 0x1000000U
|
||||
#include <stdint.h>
|
||||
void debug_log( const char *fmt, ...) ;
|
||||
@@ -622,7 +623,7 @@ extern const int be_default_protocol;
|
||||
* Name of this particular application, for use in the config box
|
||||
* and other pieces of text.
|
||||
*/
|
||||
#if (defined MOD_PERSO) && (!defined FDJ)
|
||||
#if (defined MOD_PERSO) && (!defined FLJ)
|
||||
extern char *appname;
|
||||
#else
|
||||
extern const char *const appname;
|
||||
@@ -686,19 +687,7 @@ GLOBAL char *cmdline_session_name;
|
||||
typedef struct {
|
||||
char *prompt;
|
||||
bool echo;
|
||||
/*
|
||||
* 'result' must be a dynamically allocated array of exactly
|
||||
* 'resultsize' chars. The code for actually reading input may
|
||||
* realloc it bigger (and adjust resultsize accordingly) if it has
|
||||
* to. The caller should free it again when finished with it.
|
||||
*
|
||||
* If resultsize==0, then result may be NULL. When setting up a
|
||||
* prompt_t, it's therefore easiest to initialise them this way,
|
||||
* which means all actual allocation is done by the callee. This
|
||||
* is what add_prompt does.
|
||||
*/
|
||||
char *result;
|
||||
size_t resultsize;
|
||||
strbuf *result;
|
||||
} prompt_t;
|
||||
typedef struct {
|
||||
/*
|
||||
@@ -729,11 +718,11 @@ typedef struct {
|
||||
void *data; /* slot for housekeeping data, managed by
|
||||
* seat_get_userpass_input(); initially NULL */
|
||||
} prompts_t;
|
||||
prompts_t *new_prompts();
|
||||
prompts_t *new_prompts(void);
|
||||
void add_prompt(prompts_t *p, char *promptstr, bool echo);
|
||||
void prompt_set_result(prompt_t *pr, const char *newstr);
|
||||
void prompt_ensure_result_size(prompt_t *pr, int len);
|
||||
/* Burn the evidence. (Assumes _all_ strings want free()ing.) */
|
||||
char *prompt_get_result(prompt_t *pr);
|
||||
const char *prompt_get_result_ref(prompt_t *pr);
|
||||
void free_prompts(prompts_t *p);
|
||||
|
||||
/*
|
||||
@@ -1065,7 +1054,7 @@ static inline bool seat_set_trust_status(Seat *seat, bool trusted)
|
||||
/* Unlike the seat's actual method, the public entry point
|
||||
* seat_connection_fatal is a wrapper function with a printf-like API,
|
||||
* defined in misc.c. */
|
||||
void seat_connection_fatal(Seat *seat, const char *fmt, ...);
|
||||
void seat_connection_fatal(Seat *seat, const char *fmt, ...) PRINTF_LIKE(2, 3);
|
||||
|
||||
/* Handy aliases for seat_output which set is_stderr to a fixed value. */
|
||||
static inline size_t seat_stdout(Seat *seat, const void *data, size_t len)
|
||||
@@ -1286,8 +1275,8 @@ static inline bool win_is_utf8(TermWin *win)
|
||||
/*
|
||||
* Global functions not specific to a connection instance.
|
||||
*/
|
||||
void nonfatal(const char *, ...);
|
||||
NORETURN void modalfatalbox(const char *, ...);
|
||||
void nonfatal(const char *, ...) PRINTF_LIKE(1, 2);
|
||||
NORETURN void modalfatalbox(const char *, ...) PRINTF_LIKE(1, 2);
|
||||
NORETURN void cleanup_exit(int);
|
||||
|
||||
/*
|
||||
@@ -1324,6 +1313,7 @@ NORETURN void cleanup_exit(int);
|
||||
X(BOOL, NONE, compression) \
|
||||
X(INT, INT, ssh_kexlist) \
|
||||
X(INT, INT, ssh_hklist) \
|
||||
X(BOOL, NONE, ssh_prefer_known_hostkeys) \
|
||||
X(INT, NONE, ssh_rekey_time) /* in minutes */ \
|
||||
X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \
|
||||
X(BOOL, NONE, tryagent) \
|
||||
@@ -1558,6 +1548,7 @@ NORETURN void cleanup_exit(int);
|
||||
X(INT, NONE, winscpprot) \
|
||||
X(STR, NONE, sftpconnect) \
|
||||
X(STR, NONE, pscpoptions) \
|
||||
X(STR, NONE, pscpshell) \
|
||||
X(STR, NONE, pscpremotedir) \
|
||||
X(STR, NONE, winscpoptions) \
|
||||
X(STR, NONE, winscprawsettings) \
|
||||
@@ -1580,6 +1571,7 @@ NORETURN void cleanup_exit(int);
|
||||
X(STR, NONE, comment) \
|
||||
X(INT, NONE, scp_auto_pwd) \
|
||||
X(BOOL, NONE, no_focus_rep) /* totally disable mouse reporting */ \
|
||||
X(INT, NONE, scrolllines) /* Options for Scroll Lines per Wheel */ \
|
||||
/* #endif */ \
|
||||
/* #ifdef MOD_PRINTCLIP */ \
|
||||
X(INT, NONE, printclip) \
|
||||
@@ -1599,7 +1591,7 @@ NORETURN void cleanup_exit(int);
|
||||
X(STR, NONE, script_waitfor) \
|
||||
X(STR, NONE, script_halton) \
|
||||
/* #endif */ \
|
||||
/* #if (defined MOD_BACKGROUNDIMAGE) && (!defined FDJ) */\
|
||||
/* #if (defined MOD_BACKGROUNDIMAGE) && (!defined FLJ) */\
|
||||
X(INT, NONE, bg_opacity) \
|
||||
X(INT, NONE, bg_slideshow) \
|
||||
X(INT, NONE, bg_type) /* 0=solid 1=wallpaper 2=bitmap */ \
|
||||
@@ -1894,7 +1886,7 @@ void logfclose(LogContext *logctx);
|
||||
void logtraffic(LogContext *logctx, unsigned char c, int logmode);
|
||||
void logflush(LogContext *logctx);
|
||||
void logevent(LogContext *logctx, const char *event);
|
||||
void logeventf(LogContext *logctx, const char *fmt, ...);
|
||||
void logeventf(LogContext *logctx, const char *fmt, ...) PRINTF_LIKE(2, 3);
|
||||
void logeventvf(LogContext *logctx, const char *fmt, va_list ap);
|
||||
|
||||
/*
|
||||
@@ -2117,7 +2109,8 @@ bool is_interactive(void);
|
||||
void console_print_error_msg(const char *prefix, const char *msg);
|
||||
void console_print_error_msg_fmt_v(
|
||||
const char *prefix, const char *fmt, va_list ap);
|
||||
void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...);
|
||||
void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...)
|
||||
PRINTF_LIKE(2, 3);
|
||||
|
||||
/*
|
||||
* Exports from printing.c.
|
||||
@@ -2155,7 +2148,7 @@ bool cmdline_host_ok(Conf *);
|
||||
#define TOOLTYPE_PORT_ARG 64
|
||||
extern int cmdline_tooltype;
|
||||
|
||||
void cmdline_error(const char *, ...);
|
||||
void cmdline_error(const char *, ...) PRINTF_LIKE(1, 2);
|
||||
|
||||
/*
|
||||
* Exports from config.c.
|
||||
@@ -1,426 +1,428 @@
|
||||
/*
|
||||
* Rlogin backend.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
#define RLOGIN_MAX_BACKLOG 4096
|
||||
|
||||
typedef struct Rlogin Rlogin;
|
||||
struct Rlogin {
|
||||
Socket *s;
|
||||
bool closed_on_socket_error;
|
||||
int bufsize;
|
||||
bool firstbyte;
|
||||
bool cansize;
|
||||
int term_width, term_height;
|
||||
Seat *seat;
|
||||
LogContext *logctx;
|
||||
|
||||
Conf *conf;
|
||||
|
||||
/* In case we need to read a username from the terminal before starting */
|
||||
prompts_t *prompt;
|
||||
|
||||
Plug plug;
|
||||
Backend backend;
|
||||
};
|
||||
|
||||
static void c_write(Rlogin *rlogin, const void *buf, size_t len)
|
||||
{
|
||||
size_t backlog = seat_stdout(rlogin->seat, buf, len);
|
||||
sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
|
||||
}
|
||||
|
||||
static void rlogin_log(Plug *plug, int type, SockAddr *addr, int port,
|
||||
const char *error_msg, int error_code)
|
||||
{
|
||||
Rlogin *rlogin = container_of(plug, Rlogin, plug);
|
||||
backend_socket_log(rlogin->seat, rlogin->logctx, type, addr, port,
|
||||
error_msg, error_code,
|
||||
rlogin->conf, !rlogin->firstbyte);
|
||||
}
|
||||
|
||||
static void rlogin_closing(Plug *plug, const char *error_msg, int error_code,
|
||||
bool calling_back)
|
||||
{
|
||||
Rlogin *rlogin = container_of(plug, Rlogin, plug);
|
||||
|
||||
/*
|
||||
* We don't implement independent EOF in each direction for Telnet
|
||||
* connections; as soon as we get word that the remote side has
|
||||
* sent us EOF, we wind up the whole connection.
|
||||
*/
|
||||
|
||||
if (rlogin->s) {
|
||||
sk_close(rlogin->s);
|
||||
rlogin->s = NULL;
|
||||
if (error_msg)
|
||||
rlogin->closed_on_socket_error = true;
|
||||
seat_notify_remote_exit(rlogin->seat);
|
||||
}
|
||||
if (error_msg) {
|
||||
/* A socket error has occurred. */
|
||||
logevent(rlogin->logctx, error_msg);
|
||||
seat_connection_fatal(rlogin->seat, "%s", error_msg);
|
||||
} /* Otherwise, the remote side closed the connection normally. */
|
||||
}
|
||||
|
||||
static void rlogin_receive(
|
||||
Plug *plug, int urgent, const char *data, size_t len)
|
||||
{
|
||||
Rlogin *rlogin = container_of(plug, Rlogin, plug);
|
||||
if (len == 0)
|
||||
return;
|
||||
if (urgent == 2) {
|
||||
char c;
|
||||
|
||||
c = *data++;
|
||||
len--;
|
||||
if (c == '\x80') {
|
||||
rlogin->cansize = true;
|
||||
backend_size(&rlogin->backend,
|
||||
rlogin->term_width, rlogin->term_height);
|
||||
}
|
||||
/*
|
||||
* We should flush everything (aka Telnet SYNCH) if we see
|
||||
* 0x02, and we should turn off and on _local_ flow control
|
||||
* on 0x10 and 0x20 respectively. I'm not convinced it's
|
||||
* worth it...
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
* Main rlogin protocol. This is really simple: the first
|
||||
* byte is expected to be NULL and is ignored, and the rest
|
||||
* is printed.
|
||||
*/
|
||||
if (rlogin->firstbyte) {
|
||||
if (data[0] == '\0') {
|
||||
data++;
|
||||
len--;
|
||||
}
|
||||
rlogin->firstbyte = false;
|
||||
}
|
||||
if (len > 0)
|
||||
c_write(rlogin, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void rlogin_sent(Plug *plug, size_t bufsize)
|
||||
{
|
||||
Rlogin *rlogin = container_of(plug, Rlogin, plug);
|
||||
rlogin->bufsize = bufsize;
|
||||
}
|
||||
|
||||
static void rlogin_startup(Rlogin *rlogin, const char *ruser)
|
||||
{
|
||||
char z = 0;
|
||||
char *p;
|
||||
|
||||
sk_write(rlogin->s, &z, 1);
|
||||
p = conf_get_str(rlogin->conf, CONF_localusername);
|
||||
sk_write(rlogin->s, p, strlen(p));
|
||||
sk_write(rlogin->s, &z, 1);
|
||||
sk_write(rlogin->s, ruser, strlen(ruser));
|
||||
sk_write(rlogin->s, &z, 1);
|
||||
p = conf_get_str(rlogin->conf, CONF_termtype);
|
||||
sk_write(rlogin->s, p, strlen(p));
|
||||
sk_write(rlogin->s, "/", 1);
|
||||
p = conf_get_str(rlogin->conf, CONF_termspeed);
|
||||
sk_write(rlogin->s, p, strspn(p, "0123456789"));
|
||||
rlogin->bufsize = sk_write(rlogin->s, &z, 1);
|
||||
|
||||
rlogin->prompt = NULL;
|
||||
}
|
||||
|
||||
static const PlugVtable Rlogin_plugvt = {
|
||||
rlogin_log,
|
||||
rlogin_closing,
|
||||
rlogin_receive,
|
||||
rlogin_sent
|
||||
};
|
||||
|
||||
/*
|
||||
* Called to set up the rlogin connection.
|
||||
*
|
||||
* Returns an error message, or NULL on success.
|
||||
*
|
||||
* Also places the canonical host name into `realhost'. It must be
|
||||
* freed by the caller.
|
||||
*/
|
||||
static const char *rlogin_init(Seat *seat, Backend **backend_handle,
|
||||
LogContext *logctx, Conf *conf,
|
||||
const char *host, int port, char **realhost,
|
||||
bool nodelay, bool keepalive)
|
||||
{
|
||||
SockAddr *addr;
|
||||
const char *err;
|
||||
Rlogin *rlogin;
|
||||
char *ruser;
|
||||
int addressfamily;
|
||||
char *loghost;
|
||||
|
||||
rlogin = snew(Rlogin);
|
||||
rlogin->plug.vt = &Rlogin_plugvt;
|
||||
rlogin->backend.vt = &rlogin_backend;
|
||||
rlogin->s = NULL;
|
||||
rlogin->closed_on_socket_error = false;
|
||||
rlogin->seat = seat;
|
||||
rlogin->logctx = logctx;
|
||||
rlogin->term_width = conf_get_int(conf, CONF_width);
|
||||
rlogin->term_height = conf_get_int(conf, CONF_height);
|
||||
rlogin->firstbyte = true;
|
||||
rlogin->cansize = false;
|
||||
rlogin->prompt = NULL;
|
||||
rlogin->conf = conf_copy(conf);
|
||||
*backend_handle = &rlogin->backend;
|
||||
|
||||
addressfamily = conf_get_int(conf, CONF_addressfamily);
|
||||
/*
|
||||
* Try to find host.
|
||||
*/
|
||||
addr = name_lookup(host, port, realhost, conf, addressfamily,
|
||||
rlogin->logctx, "rlogin connection");
|
||||
if ((err = sk_addr_error(addr)) != NULL) {
|
||||
sk_addr_free(addr);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (port < 0)
|
||||
port = 513; /* default rlogin port */
|
||||
|
||||
/*
|
||||
* Open socket.
|
||||
*/
|
||||
rlogin->s = new_connection(addr, *realhost, port, true, false,
|
||||
nodelay, keepalive, &rlogin->plug, conf);
|
||||
if ((err = sk_socket_error(rlogin->s)) != NULL)
|
||||
return err;
|
||||
|
||||
loghost = conf_get_str(conf, CONF_loghost);
|
||||
if (*loghost) {
|
||||
char *colon;
|
||||
|
||||
sfree(*realhost);
|
||||
*realhost = dupstr(loghost);
|
||||
|
||||
colon = host_strrchr(*realhost, ':');
|
||||
if (colon)
|
||||
*colon++ = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Send local username, remote username, terminal type and
|
||||
* terminal speed - unless we don't have the remote username yet,
|
||||
* in which case we prompt for it and may end up deferring doing
|
||||
* anything else until the local prompt mechanism returns.
|
||||
*/
|
||||
if ((ruser = get_remote_username(conf)) != NULL) {
|
||||
/* Next terminal output will come from server */
|
||||
seat_set_trust_status(rlogin->seat, false);
|
||||
rlogin_startup(rlogin, ruser);
|
||||
sfree(ruser);
|
||||
} else {
|
||||
int ret;
|
||||
|
||||
rlogin->prompt = new_prompts();
|
||||
rlogin->prompt->to_server = true;
|
||||
rlogin->prompt->from_server = false;
|
||||
rlogin->prompt->name = dupstr("Rlogin login name");
|
||||
add_prompt(rlogin->prompt, dupstr("rlogin username: "), true);
|
||||
ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, NULL);
|
||||
if (ret >= 0) {
|
||||
/* Next terminal output will come from server */
|
||||
seat_set_trust_status(rlogin->seat, false);
|
||||
rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rlogin_free(Backend *be)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
|
||||
if (rlogin->prompt)
|
||||
free_prompts(rlogin->prompt);
|
||||
if (rlogin->s)
|
||||
sk_close(rlogin->s);
|
||||
conf_free(rlogin->conf);
|
||||
sfree(rlogin);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stub routine (we don't have any need to reconfigure this backend).
|
||||
*/
|
||||
static void rlogin_reconfig(Backend *be, Conf *conf)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to send data down the rlogin connection.
|
||||
*/
|
||||
static size_t rlogin_send(Backend *be, const char *buf, size_t len)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
bufchain bc;
|
||||
|
||||
if (rlogin->s == NULL)
|
||||
return 0;
|
||||
|
||||
bufchain_init(&bc);
|
||||
bufchain_add(&bc, buf, len);
|
||||
|
||||
if (rlogin->prompt) {
|
||||
/*
|
||||
* We're still prompting for a username, and aren't talking
|
||||
* directly to the network connection yet.
|
||||
*/
|
||||
int ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, &bc);
|
||||
if (ret >= 0) {
|
||||
/* Next terminal output will come from server */
|
||||
seat_set_trust_status(rlogin->seat, false);
|
||||
rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result);
|
||||
/* that nulls out rlogin->prompt, so then we'll start sending
|
||||
* data down the wire in the obvious way */
|
||||
}
|
||||
}
|
||||
|
||||
if (!rlogin->prompt) {
|
||||
while (bufchain_size(&bc) > 0) {
|
||||
ptrlen data = bufchain_prefix(&bc);
|
||||
rlogin->bufsize = sk_write(rlogin->s, data.ptr, data.len);
|
||||
bufchain_consume(&bc, len);
|
||||
}
|
||||
}
|
||||
|
||||
bufchain_clear(&bc);
|
||||
|
||||
return rlogin->bufsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to query the current socket sendability status.
|
||||
*/
|
||||
static size_t rlogin_sendbuffer(Backend *be)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
return rlogin->bufsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to set the size of the window
|
||||
*/
|
||||
static void rlogin_size(Backend *be, int width, int height)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
char b[12] = { '\xFF', '\xFF', 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
rlogin->term_width = width;
|
||||
rlogin->term_height = height;
|
||||
|
||||
if (rlogin->s == NULL || !rlogin->cansize)
|
||||
return;
|
||||
|
||||
b[6] = rlogin->term_width >> 8;
|
||||
b[7] = rlogin->term_width & 0xFF;
|
||||
b[4] = rlogin->term_height >> 8;
|
||||
b[5] = rlogin->term_height & 0xFF;
|
||||
rlogin->bufsize = sk_write(rlogin->s, b, 12);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send rlogin special codes.
|
||||
*/
|
||||
static void rlogin_special(Backend *be, SessionSpecialCode code, int arg)
|
||||
{
|
||||
/* Do nothing! */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a list of the special codes that make sense in this
|
||||
* protocol.
|
||||
*/
|
||||
static const SessionSpecial *rlogin_get_specials(Backend *be)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool rlogin_connected(Backend *be)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
return rlogin->s != NULL;
|
||||
}
|
||||
|
||||
static bool rlogin_sendok(Backend *be)
|
||||
{
|
||||
/* Rlogin *rlogin = container_of(be, Rlogin, backend); */
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rlogin_unthrottle(Backend *be, size_t backlog)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
|
||||
}
|
||||
|
||||
static bool rlogin_ldisc(Backend *be, int option)
|
||||
{
|
||||
/* Rlogin *rlogin = container_of(be, Rlogin, backend); */
|
||||
return false;
|
||||
}
|
||||
|
||||
static void rlogin_provide_ldisc(Backend *be, Ldisc *ldisc)
|
||||
{
|
||||
/* This is a stub. */
|
||||
}
|
||||
|
||||
static int rlogin_exitcode(Backend *be)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
if (rlogin->s != NULL)
|
||||
return -1; /* still connected */
|
||||
else if (rlogin->closed_on_socket_error)
|
||||
return INT_MAX; /* a socket error counts as an unclean exit */
|
||||
else
|
||||
/* If we ever implement RSH, we'll probably need to do this properly */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cfg_info for rlogin does nothing at all.
|
||||
*/
|
||||
static int rlogin_cfg_info(Backend *be)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct BackendVtable rlogin_backend = {
|
||||
rlogin_init,
|
||||
rlogin_free,
|
||||
rlogin_reconfig,
|
||||
rlogin_send,
|
||||
rlogin_sendbuffer,
|
||||
rlogin_size,
|
||||
rlogin_special,
|
||||
rlogin_get_specials,
|
||||
rlogin_connected,
|
||||
rlogin_exitcode,
|
||||
rlogin_sendok,
|
||||
rlogin_ldisc,
|
||||
rlogin_provide_ldisc,
|
||||
rlogin_unthrottle,
|
||||
rlogin_cfg_info,
|
||||
NULL /* test_for_upstream */,
|
||||
"rlogin",
|
||||
PROT_RLOGIN,
|
||||
513
|
||||
};
|
||||
/*
|
||||
* Rlogin backend.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
#define RLOGIN_MAX_BACKLOG 4096
|
||||
|
||||
typedef struct Rlogin Rlogin;
|
||||
struct Rlogin {
|
||||
Socket *s;
|
||||
bool closed_on_socket_error;
|
||||
int bufsize;
|
||||
bool firstbyte;
|
||||
bool cansize;
|
||||
int term_width, term_height;
|
||||
Seat *seat;
|
||||
LogContext *logctx;
|
||||
|
||||
Conf *conf;
|
||||
|
||||
/* In case we need to read a username from the terminal before starting */
|
||||
prompts_t *prompt;
|
||||
|
||||
Plug plug;
|
||||
Backend backend;
|
||||
};
|
||||
|
||||
static void c_write(Rlogin *rlogin, const void *buf, size_t len)
|
||||
{
|
||||
size_t backlog = seat_stdout(rlogin->seat, buf, len);
|
||||
sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
|
||||
}
|
||||
|
||||
static void rlogin_log(Plug *plug, int type, SockAddr *addr, int port,
|
||||
const char *error_msg, int error_code)
|
||||
{
|
||||
Rlogin *rlogin = container_of(plug, Rlogin, plug);
|
||||
backend_socket_log(rlogin->seat, rlogin->logctx, type, addr, port,
|
||||
error_msg, error_code,
|
||||
rlogin->conf, !rlogin->firstbyte);
|
||||
}
|
||||
|
||||
static void rlogin_closing(Plug *plug, const char *error_msg, int error_code,
|
||||
bool calling_back)
|
||||
{
|
||||
Rlogin *rlogin = container_of(plug, Rlogin, plug);
|
||||
|
||||
/*
|
||||
* We don't implement independent EOF in each direction for Telnet
|
||||
* connections; as soon as we get word that the remote side has
|
||||
* sent us EOF, we wind up the whole connection.
|
||||
*/
|
||||
|
||||
if (rlogin->s) {
|
||||
sk_close(rlogin->s);
|
||||
rlogin->s = NULL;
|
||||
if (error_msg)
|
||||
rlogin->closed_on_socket_error = true;
|
||||
seat_notify_remote_exit(rlogin->seat);
|
||||
}
|
||||
if (error_msg) {
|
||||
/* A socket error has occurred. */
|
||||
logevent(rlogin->logctx, error_msg);
|
||||
seat_connection_fatal(rlogin->seat, "%s", error_msg);
|
||||
} /* Otherwise, the remote side closed the connection normally. */
|
||||
}
|
||||
|
||||
static void rlogin_receive(
|
||||
Plug *plug, int urgent, const char *data, size_t len)
|
||||
{
|
||||
Rlogin *rlogin = container_of(plug, Rlogin, plug);
|
||||
if (len == 0)
|
||||
return;
|
||||
if (urgent == 2) {
|
||||
char c;
|
||||
|
||||
c = *data++;
|
||||
len--;
|
||||
if (c == '\x80') {
|
||||
rlogin->cansize = true;
|
||||
backend_size(&rlogin->backend,
|
||||
rlogin->term_width, rlogin->term_height);
|
||||
}
|
||||
/*
|
||||
* We should flush everything (aka Telnet SYNCH) if we see
|
||||
* 0x02, and we should turn off and on _local_ flow control
|
||||
* on 0x10 and 0x20 respectively. I'm not convinced it's
|
||||
* worth it...
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
* Main rlogin protocol. This is really simple: the first
|
||||
* byte is expected to be NULL and is ignored, and the rest
|
||||
* is printed.
|
||||
*/
|
||||
if (rlogin->firstbyte) {
|
||||
if (data[0] == '\0') {
|
||||
data++;
|
||||
len--;
|
||||
}
|
||||
rlogin->firstbyte = false;
|
||||
}
|
||||
if (len > 0)
|
||||
c_write(rlogin, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void rlogin_sent(Plug *plug, size_t bufsize)
|
||||
{
|
||||
Rlogin *rlogin = container_of(plug, Rlogin, plug);
|
||||
rlogin->bufsize = bufsize;
|
||||
}
|
||||
|
||||
static void rlogin_startup(Rlogin *rlogin, const char *ruser)
|
||||
{
|
||||
char z = 0;
|
||||
char *p;
|
||||
|
||||
sk_write(rlogin->s, &z, 1);
|
||||
p = conf_get_str(rlogin->conf, CONF_localusername);
|
||||
sk_write(rlogin->s, p, strlen(p));
|
||||
sk_write(rlogin->s, &z, 1);
|
||||
sk_write(rlogin->s, ruser, strlen(ruser));
|
||||
sk_write(rlogin->s, &z, 1);
|
||||
p = conf_get_str(rlogin->conf, CONF_termtype);
|
||||
sk_write(rlogin->s, p, strlen(p));
|
||||
sk_write(rlogin->s, "/", 1);
|
||||
p = conf_get_str(rlogin->conf, CONF_termspeed);
|
||||
sk_write(rlogin->s, p, strspn(p, "0123456789"));
|
||||
rlogin->bufsize = sk_write(rlogin->s, &z, 1);
|
||||
|
||||
rlogin->prompt = NULL;
|
||||
}
|
||||
|
||||
static const PlugVtable Rlogin_plugvt = {
|
||||
rlogin_log,
|
||||
rlogin_closing,
|
||||
rlogin_receive,
|
||||
rlogin_sent
|
||||
};
|
||||
|
||||
/*
|
||||
* Called to set up the rlogin connection.
|
||||
*
|
||||
* Returns an error message, or NULL on success.
|
||||
*
|
||||
* Also places the canonical host name into `realhost'. It must be
|
||||
* freed by the caller.
|
||||
*/
|
||||
static const char *rlogin_init(Seat *seat, Backend **backend_handle,
|
||||
LogContext *logctx, Conf *conf,
|
||||
const char *host, int port, char **realhost,
|
||||
bool nodelay, bool keepalive)
|
||||
{
|
||||
SockAddr *addr;
|
||||
const char *err;
|
||||
Rlogin *rlogin;
|
||||
char *ruser;
|
||||
int addressfamily;
|
||||
char *loghost;
|
||||
|
||||
rlogin = snew(Rlogin);
|
||||
rlogin->plug.vt = &Rlogin_plugvt;
|
||||
rlogin->backend.vt = &rlogin_backend;
|
||||
rlogin->s = NULL;
|
||||
rlogin->closed_on_socket_error = false;
|
||||
rlogin->seat = seat;
|
||||
rlogin->logctx = logctx;
|
||||
rlogin->term_width = conf_get_int(conf, CONF_width);
|
||||
rlogin->term_height = conf_get_int(conf, CONF_height);
|
||||
rlogin->firstbyte = true;
|
||||
rlogin->cansize = false;
|
||||
rlogin->prompt = NULL;
|
||||
rlogin->conf = conf_copy(conf);
|
||||
*backend_handle = &rlogin->backend;
|
||||
|
||||
addressfamily = conf_get_int(conf, CONF_addressfamily);
|
||||
/*
|
||||
* Try to find host.
|
||||
*/
|
||||
addr = name_lookup(host, port, realhost, conf, addressfamily,
|
||||
rlogin->logctx, "rlogin connection");
|
||||
if ((err = sk_addr_error(addr)) != NULL) {
|
||||
sk_addr_free(addr);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (port < 0)
|
||||
port = 513; /* default rlogin port */
|
||||
|
||||
/*
|
||||
* Open socket.
|
||||
*/
|
||||
rlogin->s = new_connection(addr, *realhost, port, true, false,
|
||||
nodelay, keepalive, &rlogin->plug, conf);
|
||||
if ((err = sk_socket_error(rlogin->s)) != NULL)
|
||||
return err;
|
||||
|
||||
loghost = conf_get_str(conf, CONF_loghost);
|
||||
if (*loghost) {
|
||||
char *colon;
|
||||
|
||||
sfree(*realhost);
|
||||
*realhost = dupstr(loghost);
|
||||
|
||||
colon = host_strrchr(*realhost, ':');
|
||||
if (colon)
|
||||
*colon++ = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Send local username, remote username, terminal type and
|
||||
* terminal speed - unless we don't have the remote username yet,
|
||||
* in which case we prompt for it and may end up deferring doing
|
||||
* anything else until the local prompt mechanism returns.
|
||||
*/
|
||||
if ((ruser = get_remote_username(conf)) != NULL) {
|
||||
/* Next terminal output will come from server */
|
||||
seat_set_trust_status(rlogin->seat, false);
|
||||
rlogin_startup(rlogin, ruser);
|
||||
sfree(ruser);
|
||||
} else {
|
||||
int ret;
|
||||
|
||||
rlogin->prompt = new_prompts();
|
||||
rlogin->prompt->to_server = true;
|
||||
rlogin->prompt->from_server = false;
|
||||
rlogin->prompt->name = dupstr("Rlogin login name");
|
||||
add_prompt(rlogin->prompt, dupstr("rlogin username: "), true);
|
||||
ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, NULL);
|
||||
if (ret >= 0) {
|
||||
/* Next terminal output will come from server */
|
||||
seat_set_trust_status(rlogin->seat, false);
|
||||
rlogin_startup(rlogin, prompt_get_result_ref(
|
||||
rlogin->prompt->prompts[0]));
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rlogin_free(Backend *be)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
|
||||
if (rlogin->prompt)
|
||||
free_prompts(rlogin->prompt);
|
||||
if (rlogin->s)
|
||||
sk_close(rlogin->s);
|
||||
conf_free(rlogin->conf);
|
||||
sfree(rlogin);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stub routine (we don't have any need to reconfigure this backend).
|
||||
*/
|
||||
static void rlogin_reconfig(Backend *be, Conf *conf)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to send data down the rlogin connection.
|
||||
*/
|
||||
static size_t rlogin_send(Backend *be, const char *buf, size_t len)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
bufchain bc;
|
||||
|
||||
if (rlogin->s == NULL)
|
||||
return 0;
|
||||
|
||||
bufchain_init(&bc);
|
||||
bufchain_add(&bc, buf, len);
|
||||
|
||||
if (rlogin->prompt) {
|
||||
/*
|
||||
* We're still prompting for a username, and aren't talking
|
||||
* directly to the network connection yet.
|
||||
*/
|
||||
int ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, &bc);
|
||||
if (ret >= 0) {
|
||||
/* Next terminal output will come from server */
|
||||
seat_set_trust_status(rlogin->seat, false);
|
||||
rlogin_startup(rlogin, prompt_get_result_ref(
|
||||
rlogin->prompt->prompts[0]));
|
||||
/* that nulls out rlogin->prompt, so then we'll start sending
|
||||
* data down the wire in the obvious way */
|
||||
}
|
||||
}
|
||||
|
||||
if (!rlogin->prompt) {
|
||||
while (bufchain_size(&bc) > 0) {
|
||||
ptrlen data = bufchain_prefix(&bc);
|
||||
rlogin->bufsize = sk_write(rlogin->s, data.ptr, data.len);
|
||||
bufchain_consume(&bc, len);
|
||||
}
|
||||
}
|
||||
|
||||
bufchain_clear(&bc);
|
||||
|
||||
return rlogin->bufsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to query the current socket sendability status.
|
||||
*/
|
||||
static size_t rlogin_sendbuffer(Backend *be)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
return rlogin->bufsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to set the size of the window
|
||||
*/
|
||||
static void rlogin_size(Backend *be, int width, int height)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
char b[12] = { '\xFF', '\xFF', 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
rlogin->term_width = width;
|
||||
rlogin->term_height = height;
|
||||
|
||||
if (rlogin->s == NULL || !rlogin->cansize)
|
||||
return;
|
||||
|
||||
b[6] = rlogin->term_width >> 8;
|
||||
b[7] = rlogin->term_width & 0xFF;
|
||||
b[4] = rlogin->term_height >> 8;
|
||||
b[5] = rlogin->term_height & 0xFF;
|
||||
rlogin->bufsize = sk_write(rlogin->s, b, 12);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send rlogin special codes.
|
||||
*/
|
||||
static void rlogin_special(Backend *be, SessionSpecialCode code, int arg)
|
||||
{
|
||||
/* Do nothing! */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a list of the special codes that make sense in this
|
||||
* protocol.
|
||||
*/
|
||||
static const SessionSpecial *rlogin_get_specials(Backend *be)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool rlogin_connected(Backend *be)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
return rlogin->s != NULL;
|
||||
}
|
||||
|
||||
static bool rlogin_sendok(Backend *be)
|
||||
{
|
||||
/* Rlogin *rlogin = container_of(be, Rlogin, backend); */
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rlogin_unthrottle(Backend *be, size_t backlog)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
|
||||
}
|
||||
|
||||
static bool rlogin_ldisc(Backend *be, int option)
|
||||
{
|
||||
/* Rlogin *rlogin = container_of(be, Rlogin, backend); */
|
||||
return false;
|
||||
}
|
||||
|
||||
static void rlogin_provide_ldisc(Backend *be, Ldisc *ldisc)
|
||||
{
|
||||
/* This is a stub. */
|
||||
}
|
||||
|
||||
static int rlogin_exitcode(Backend *be)
|
||||
{
|
||||
Rlogin *rlogin = container_of(be, Rlogin, backend);
|
||||
if (rlogin->s != NULL)
|
||||
return -1; /* still connected */
|
||||
else if (rlogin->closed_on_socket_error)
|
||||
return INT_MAX; /* a socket error counts as an unclean exit */
|
||||
else
|
||||
/* If we ever implement RSH, we'll probably need to do this properly */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cfg_info for rlogin does nothing at all.
|
||||
*/
|
||||
static int rlogin_cfg_info(Backend *be)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct BackendVtable rlogin_backend = {
|
||||
rlogin_init,
|
||||
rlogin_free,
|
||||
rlogin_reconfig,
|
||||
rlogin_send,
|
||||
rlogin_sendbuffer,
|
||||
rlogin_size,
|
||||
rlogin_special,
|
||||
rlogin_get_specials,
|
||||
rlogin_connected,
|
||||
rlogin_exitcode,
|
||||
rlogin_sendok,
|
||||
rlogin_ldisc,
|
||||
rlogin_provide_ldisc,
|
||||
rlogin_unthrottle,
|
||||
rlogin_cfg_info,
|
||||
NULL /* test_for_upstream */,
|
||||
"rlogin",
|
||||
PROT_RLOGIN,
|
||||
513
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -368,7 +368,7 @@ static int xfwd_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
|
||||
chan = portfwd_raw_new(sess->c->cl, &plug);
|
||||
s = constructor(ctx, plug);
|
||||
if ((err = sk_socket_error(s)) != NULL) {
|
||||
portfwd_raw_free(chan);
|
||||
portfwd_raw_free(chan);
|
||||
return 1;
|
||||
}
|
||||
pi = sk_peer_info(s);
|
||||
@@ -444,7 +444,7 @@ static int agentfwd_accepting(
|
||||
chan = portfwd_raw_new(sess->c->cl, &plug);
|
||||
s = constructor(ctx, plug);
|
||||
if ((err = sk_socket_error(s)) != NULL) {
|
||||
portfwd_raw_free(chan);
|
||||
portfwd_raw_free(chan);
|
||||
return 1;
|
||||
}
|
||||
portfwd_raw_setup(chan, s, ssh_serverside_agent_open(sess->c->cl, chan));
|
||||
@@ -640,10 +640,10 @@ static void sesschan_notify_remote_exit(Seat *seat)
|
||||
sshfwd_send_exit_signal(
|
||||
sess->c, signame, false, ptrlen_from_asciz(sigmsg));
|
||||
|
||||
sfree(sigmsg);
|
||||
|
||||
got_signal = true;
|
||||
}
|
||||
|
||||
sfree(sigmsg);
|
||||
} else {
|
||||
int signum = pty_backend_exit_signum(sess->backend);
|
||||
|
||||
@@ -21,13 +21,17 @@
|
||||
#endif
|
||||
|
||||
#ifdef MOD_PERSO
|
||||
#ifndef SAVEMODE_DIR
|
||||
#define SAVEMODE_DIR 2
|
||||
#endif
|
||||
extern char FileExtension[15] ;
|
||||
extern char PassKey[1024] ;
|
||||
extern int cryptstring( char * st, const char * key ) ;
|
||||
extern int decryptstring( char * st, const char * key ) ;
|
||||
int cryptpassword( int mode, char * password, const char * host, const char * termtype ) ;
|
||||
int decryptpassword( int mode, char * password, const char * host, const char * termtype ) ;
|
||||
int get_param( const char * val ) ;
|
||||
void MASKPASS( char * password ) ;
|
||||
void MASKPASS( const int mode, char * password ) ;
|
||||
int GetBackgroundImageFlag(void) ;
|
||||
int GetCryptSaltFlag() ;
|
||||
int DebugGetPassword( Conf *conf, const char *pwd ) ;
|
||||
#endif
|
||||
|
||||
/* The cipher order given here is the default order. */
|
||||
@@ -507,8 +511,7 @@ static void write_clip_setting(settings_w *sesskey, const char *savekey,
|
||||
break;
|
||||
case CLIPUI_CUSTOM:
|
||||
{
|
||||
char *sval = dupcat("custom:", conf_get_str(conf, strconfkey),
|
||||
(const char *)NULL);
|
||||
char *sval = dupcat("custom:", conf_get_str(conf, strconfkey));
|
||||
write_setting_s(sesskey, savekey, sval);
|
||||
sfree(sval);
|
||||
}
|
||||
@@ -616,6 +619,7 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
|
||||
wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist);
|
||||
wprefs(sesskey, "KEX", kexnames, KEX_MAX, conf, CONF_ssh_kexlist);
|
||||
wprefs(sesskey, "HostKey", hknames, HK_MAX, conf, CONF_ssh_hklist);
|
||||
write_setting_b(sesskey, "PreferKnownHostKeys", conf_get_bool(conf, CONF_ssh_prefer_known_hostkeys));
|
||||
write_setting_i(sesskey, "RekeyTime", conf_get_int(conf, CONF_ssh_rekey_time));
|
||||
#ifndef NO_GSSAPI
|
||||
write_setting_i(sesskey, "GssapiRekey", conf_get_int(conf, CONF_gssapirekey));
|
||||
@@ -836,7 +840,7 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
|
||||
write_setting_i(sesskey, "WakeupReconnect", conf_get_int(conf,CONF_wakeup_reconnect) );
|
||||
write_setting_i(sesskey, "FailureReconnect", conf_get_int(conf,CONF_failure_reconnect) );
|
||||
#endif
|
||||
#if (defined MOD_BACKGROUNDIMAGE) && (!defined FDJ)
|
||||
#if (defined MOD_BACKGROUNDIMAGE) && (!defined FLJ)
|
||||
if( GetBackgroundImageFlag() ) {
|
||||
write_setting_i(sesskey, "BgOpacity", conf_get_int(conf, CONF_bg_opacity) );
|
||||
write_setting_i(sesskey, "BgSlideshow", conf_get_int(conf, CONF_bg_slideshow) );
|
||||
@@ -887,6 +891,7 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
|
||||
write_setting_i(sesskey, "WinSCPProtocol", conf_get_int(conf, CONF_winscpprot) );
|
||||
write_setting_s(sesskey, "SFTPConnect", conf_get_str(conf, CONF_sftpconnect) );
|
||||
write_setting_s(sesskey, "PSCPOptions", conf_get_str(conf, CONF_pscpoptions) );
|
||||
write_setting_s(sesskey, "PSCPShell", conf_get_str(conf, CONF_pscpshell) );
|
||||
write_setting_s(sesskey, "PSCPRemoteDir", conf_get_str(conf, CONF_pscpremotedir) );
|
||||
write_setting_s(sesskey, "WinSCPOptions", conf_get_str(conf, CONF_winscpoptions) );
|
||||
write_setting_s(sesskey, "WinSCPRawSettings", conf_get_str(conf, CONF_winscprawsettings) );
|
||||
@@ -907,16 +912,11 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
|
||||
write_setting_b(sesskey, "SaveWindowPos", conf_get_bool(conf, CONF_save_windowpos) ); /* BKG */
|
||||
write_setting_b(sesskey, "ForegroundOnBell", conf_get_bool(conf, CONF_foreground_on_bell) );
|
||||
|
||||
if( (strlen(conf_get_str(conf, CONF_host))+strlen(conf_get_str(conf, CONF_termtype))) < 1000 ) {
|
||||
sprintf( PassKey, "%s%sKiTTY", conf_get_str(conf, CONF_host), conf_get_str(conf, CONF_termtype) ) ;
|
||||
} else {
|
||||
strcpy( PassKey, "" ) ;
|
||||
}
|
||||
char pst[4096] ;
|
||||
#ifndef MOD_NOPASSWORD
|
||||
char pst[4096] ;
|
||||
strcpy( pst, conf_get_str(conf, CONF_password ) );
|
||||
MASKPASS(pst);
|
||||
cryptstring( pst, PassKey ) ;
|
||||
MASKPASS(GetCryptSaltFlag(), pst);
|
||||
cryptpassword( GetCryptSaltFlag(), pst, conf_get_str(conf, CONF_host), conf_get_str(conf, CONF_termtype) ) ;
|
||||
write_setting_s(sesskey, "Password", pst);
|
||||
memset(pst,0,strlen(pst));
|
||||
#endif
|
||||
@@ -924,6 +924,7 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
|
||||
write_setting_s(sesskey, "Comment", conf_get_str(conf, CONF_comment) );
|
||||
write_setting_i(sesskey, "SCPAutoPwd", conf_get_int(conf, CONF_scp_auto_pwd));
|
||||
write_setting_b(sesskey, "NoFocusReporting", conf_get_bool(conf, CONF_no_focus_rep));
|
||||
write_setting_i(sesskey, "LinesAtAScroll", conf_get_int(conf, CONF_scrolllines));
|
||||
#endif
|
||||
#ifdef MOD_PORTKNOCKING
|
||||
write_setting_s(sesskey, "PortKnocking", conf_get_str(conf, CONF_portknockingoptions) );
|
||||
@@ -944,8 +945,34 @@ bool load_settings(const char *section, Conf *conf)
|
||||
else conf_set_str( conf, CONF_sessionname, "" ) ;
|
||||
#endif
|
||||
close_settings_r(sesskey);
|
||||
#ifdef MOD_PERSO
|
||||
if (exists && conf_launchable(conf)) {
|
||||
if( get_param("INIFILE") == SAVEMODE_DIR ) {
|
||||
char *name = NULL ;
|
||||
if( (section!=NULL) && (strlen(section)>0) && (strlen(conf_get_str( conf, CONF_folder))>0) ) {
|
||||
if( strcmp(conf_get_str( conf, CONF_folder), "Default" ) ) {
|
||||
name = (char*)malloc( strlen(section)+strlen(conf_get_str( conf, CONF_folder)) + 2 ) ;
|
||||
sprintf( name, "%s/%s", conf_get_str( conf, CONF_folder), section ) ;
|
||||
}
|
||||
} else if( (section!=NULL) && (strlen(section)>0) ) {
|
||||
name = (char*)malloc( strlen(section)+1 ) ;
|
||||
sprintf( name, "%s", section ) ;
|
||||
}
|
||||
if( name!=NULL ) {
|
||||
//add_session_to_jumplist(section) ;
|
||||
add_session_to_jumplist(name) ;
|
||||
free(name) ;
|
||||
} else {
|
||||
add_session_to_jumplist(section) ;
|
||||
}
|
||||
} else {
|
||||
add_session_to_jumplist(section);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (exists && conf_launchable(conf))
|
||||
add_session_to_jumplist(section);
|
||||
#endif
|
||||
return exists;
|
||||
}
|
||||
|
||||
@@ -1154,6 +1181,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
|
||||
}
|
||||
gprefs(sesskey, "HostKey", "ed25519,ecdsa,rsa,dsa,WARN",
|
||||
hknames, HK_MAX, conf, CONF_ssh_hklist);
|
||||
gppb(sesskey, "PreferKnownHostKeys", true, conf, CONF_ssh_prefer_known_hostkeys);
|
||||
gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time);
|
||||
#ifndef NO_GSSAPI
|
||||
gppi(sesskey, "GssapiRekey", GSS_DEF_REKEY_MINS, conf, CONF_gssapirekey);
|
||||
@@ -1231,7 +1259,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
|
||||
gppb(sesskey, "TelnetRet", true, conf, CONF_telnet_newline);
|
||||
gppi(sesskey, "LocalEcho", AUTO, conf, CONF_localecho);
|
||||
gppi(sesskey, "LocalEdit", AUTO, conf, CONF_localedit);
|
||||
#if (defined MOD_PERSO) && (!defined FDJ)
|
||||
#if (defined MOD_PERSO) && (!defined FLJ)
|
||||
gpps(sesskey, "Answerback", "KiTTY", conf, CONF_answerback);
|
||||
#else
|
||||
gpps(sesskey, "Answerback", "PuTTY", conf, CONF_answerback);
|
||||
@@ -1481,7 +1509,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
|
||||
gppi(sesskey, "WakeupReconnect", 0, conf, CONF_wakeup_reconnect );
|
||||
gppi(sesskey, "FailureReconnect", 0, conf, CONF_failure_reconnect );
|
||||
#endif
|
||||
#if (defined MOD_BACKGROUNDIMAGE) && (!defined FDJ)
|
||||
#if (defined MOD_BACKGROUNDIMAGE) && (!defined FLJ)
|
||||
gppi(sesskey, "BgOpacity", 50, conf, CONF_bg_opacity );
|
||||
gppi(sesskey, "BgSlideshow", 0, conf, CONF_bg_slideshow );
|
||||
gppi(sesskey, "BgType", 0, conf, CONF_bg_type );
|
||||
@@ -1526,6 +1554,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
|
||||
gppi(sesskey, "WinSCPProtocol", 1, conf, CONF_winscpprot );
|
||||
gpps(sesskey, "SFTPConnect", "", conf, CONF_sftpconnect );
|
||||
gpps(sesskey, "PSCPOptions", "-r", conf, CONF_pscpoptions );
|
||||
gpps(sesskey, "PSCPShell", "", conf, CONF_pscpshell );
|
||||
gpps(sesskey, "PSCPRemoteDir", "", conf, CONF_pscpremotedir );
|
||||
gpps(sesskey, "WinSCPOptions", "", conf, CONF_winscpoptions );
|
||||
gpps(sesskey, "WinSCPRawSettings", "", conf, CONF_winscprawsettings );
|
||||
@@ -1547,38 +1576,14 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
|
||||
gppb(sesskey, "SaveWindowPos", false, conf, CONF_save_windowpos ); /* BKG */
|
||||
gppb(sesskey, "ForegroundOnBell", false, conf, CONF_foreground_on_bell );
|
||||
#ifndef MOD_NOPASSWORD
|
||||
if( strlen(conf_get_str(conf, CONF_host))>0 ) {
|
||||
if( (strlen(conf_get_str(conf, CONF_host))+strlen(conf_get_str(conf, CONF_termtype))) < 1000 ) {
|
||||
sprintf( PassKey, "%s%sKiTTY", conf_get_str(conf, CONF_host), conf_get_str(conf, CONF_termtype) ) ;
|
||||
} else {
|
||||
strcpy( PassKey, "" ) ;
|
||||
}
|
||||
gpps(sesskey, "Password", "", conf, CONF_password );
|
||||
} else {
|
||||
if( strlen(conf_get_str(conf, CONF_termtype)) < 1000 ) {
|
||||
sprintf( PassKey, "%sKiTTY", conf_get_str(conf, CONF_termtype) ) ;
|
||||
} else {
|
||||
strcpy( PassKey, "" ) ;
|
||||
}
|
||||
gpps(sesskey, "Password", "", conf, CONF_password );
|
||||
}
|
||||
|
||||
gpps(sesskey, "Password", "", conf, CONF_password );
|
||||
if( strlen(conf_get_str(conf, CONF_password))>0 ) {
|
||||
char pst[4096] ;
|
||||
if( strlen(conf_get_str(conf, CONF_password))<=4095 ) { strcpy( pst, conf_get_str(conf, CONF_password) ) ; }
|
||||
else { memcpy( pst, conf_get_str( conf, CONF_password ), 4095 ) ; pst[4095]='\0'; }
|
||||
decryptstring( pst, PassKey ) ;
|
||||
if( strlen( pst ) > 0 ) {
|
||||
FILE *fp ;
|
||||
if( ( fp = fopen( "kitty.password", "r") ) != NULL ) { // Affichage en clair du password dans le fichier kitty.password si celui-ci existe
|
||||
fclose( fp ) ;
|
||||
if( ( fp = fopen( "kitty.password", "w" ) ) != NULL ) {
|
||||
fprintf( fp, "%s", pst ) ;
|
||||
fclose( fp ) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
MASKPASS(pst);
|
||||
decryptpassword( GetCryptSaltFlag(), pst, conf_get_str(conf, CONF_host), conf_get_str(conf, CONF_termtype) ) ;
|
||||
if( strlen( pst ) > 0 ) { DebugGetPassword( conf, pst ) ; }
|
||||
MASKPASS(GetCryptSaltFlag(), pst);
|
||||
conf_set_str( conf, CONF_password, pst ) ;
|
||||
memset(pst,0,strlen(pst));
|
||||
}
|
||||
@@ -1589,6 +1594,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
|
||||
gpps(sesskey, "Comment", "", conf, CONF_comment );
|
||||
gppi(sesskey, "SCPAutoPwd", 0, conf, CONF_scp_auto_pwd);
|
||||
gppb(sesskey, "NoFocusReporting", true, conf, CONF_no_focus_rep);
|
||||
gppi(sesskey, "LinesAtAScroll", -1, conf, CONF_scrolllines);
|
||||
#endif
|
||||
#ifdef MOD_PORTKNOCKING
|
||||
gpps(sesskey, "PortKnocking", "", conf, CONF_portknockingoptions );
|
||||
@@ -1677,6 +1683,44 @@ void get_sesslist(struct sesslist *list, bool allocate)
|
||||
}
|
||||
}
|
||||
#ifdef MOD_PERSO
|
||||
/* Print current password if kitty.password exists */
|
||||
int DebugGetPassword( Conf *conf, const char *pwd ) {
|
||||
FILE *fp ;
|
||||
if( pwd==NULL ) return 0;
|
||||
if( strlen(pwd) == 0 ) return 0;
|
||||
if( ( fp = fopen( "kitty.password", "r") ) != NULL ) {
|
||||
char * pst = (char*) malloc( strlen(pwd)+1 ) ;
|
||||
fclose( fp ) ;
|
||||
if( ( fp = fopen( "kitty.password", "w" ) ) != NULL ) {
|
||||
fprintf( fp, "encpass=%s\n", conf_get_str( conf, CONF_password ) ) ;
|
||||
fprintf( fp, "host=%s\n", conf_get_str( conf, CONF_host ) ) ;
|
||||
fprintf( fp, "term=%s\n", conf_get_str( conf, CONF_termtype ) ) ;
|
||||
fprintf( fp, "pass=%s\n", pwd ) ;
|
||||
strcpy( pst, pwd ) ;
|
||||
MASKPASS( GetCryptSaltFlag(), pst ) ;
|
||||
fprintf( fp, "maskpass=%s\n", pst ) ;
|
||||
MASKPASS( GetCryptSaltFlag(), pst ) ;
|
||||
fprintf( fp, "clearpass=%s\n", pst ) ;
|
||||
memset( pst, 0, strlen(pwd) ) ;
|
||||
fclose( fp ) ;
|
||||
}
|
||||
free(pst);
|
||||
return 1 ;
|
||||
}
|
||||
return 0 ;
|
||||
}
|
||||
int DebugAddPassword( const char*fct, const char*pwd ) {
|
||||
FILE *fp ;
|
||||
if( ( fp = fopen( "kitty.password", "r") ) != NULL ) {
|
||||
fclose( fp ) ;
|
||||
if( ( fp = fopen( "kitty.password", "a" ) ) != NULL ) {
|
||||
fprintf( fp, "%s=%s\n", fct, pwd ) ;
|
||||
fclose(fp) ;
|
||||
}
|
||||
return 1 ;
|
||||
}
|
||||
return 0 ;
|
||||
}
|
||||
/* Fonction de creation d'une session */
|
||||
void create_settings( const char * name ) {
|
||||
char *nname = NULL ;
|
||||
@@ -1,278 +1,279 @@
|
||||
/*
|
||||
* Implement the centralised parts of the server side of SFTP.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "sftp.h"
|
||||
|
||||
struct sftp_packet *sftp_handle_request(
|
||||
SftpServer *srv, struct sftp_packet *req)
|
||||
{
|
||||
struct sftp_packet *reply;
|
||||
unsigned id;
|
||||
ptrlen path, dstpath, handle, data;
|
||||
uint64_t offset;
|
||||
unsigned length;
|
||||
struct fxp_attrs attrs;
|
||||
DefaultSftpReplyBuilder dsrb;
|
||||
SftpReplyBuilder *rb;
|
||||
|
||||
if (req->type == SSH_FXP_INIT) {
|
||||
/*
|
||||
* Special case which doesn't have a request id at the start.
|
||||
*/
|
||||
reply = sftp_pkt_init(SSH_FXP_VERSION);
|
||||
/*
|
||||
* Since we support only the lowest protocol version, we don't
|
||||
* need to take the min of this and the client's version, or
|
||||
* even to bother reading the client version number out of the
|
||||
* input packet.
|
||||
*/
|
||||
put_uint32(reply, SFTP_PROTO_VERSION);
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*
|
||||
* Centralise the request id handling. We'll overwrite the type
|
||||
* code of the output packet later.
|
||||
*/
|
||||
id = get_uint32(req);
|
||||
reply = sftp_pkt_init(0);
|
||||
put_uint32(reply, id);
|
||||
|
||||
dsrb.rb.vt = &DefaultSftpReplyBuilder_vt;
|
||||
dsrb.pkt = reply;
|
||||
rb = &dsrb.rb;
|
||||
|
||||
switch (req->type) {
|
||||
case SSH_FXP_REALPATH:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_realpath(srv, rb, path);
|
||||
break;
|
||||
|
||||
case SSH_FXP_OPEN:
|
||||
path = get_string(req);
|
||||
flags = get_uint32(req);
|
||||
get_fxp_attrs(req, &attrs);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
if ((flags & (SSH_FXF_READ|SSH_FXF_WRITE)) == 0) {
|
||||
fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
|
||||
"open without READ or WRITE flag");
|
||||
} else if ((flags & (SSH_FXF_CREAT|SSH_FXF_TRUNC)) == SSH_FXF_TRUNC) {
|
||||
fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
|
||||
"open with TRUNC but not CREAT");
|
||||
} else if ((flags & (SSH_FXF_CREAT|SSH_FXF_EXCL)) == SSH_FXF_EXCL) {
|
||||
fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
|
||||
"open with EXCL but not CREAT");
|
||||
} else {
|
||||
sftpsrv_open(srv, rb, path, flags, attrs);
|
||||
}
|
||||
break;
|
||||
|
||||
case SSH_FXP_OPENDIR:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_opendir(srv, rb, path);
|
||||
break;
|
||||
|
||||
case SSH_FXP_CLOSE:
|
||||
handle = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_close(srv, rb, handle);
|
||||
break;
|
||||
|
||||
case SSH_FXP_MKDIR:
|
||||
path = get_string(req);
|
||||
get_fxp_attrs(req, &attrs);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_mkdir(srv, rb, path, attrs);
|
||||
break;
|
||||
|
||||
case SSH_FXP_RMDIR:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_rmdir(srv, rb, path);
|
||||
break;
|
||||
|
||||
case SSH_FXP_REMOVE:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_remove(srv, rb, path);
|
||||
break;
|
||||
|
||||
case SSH_FXP_RENAME:
|
||||
path = get_string(req);
|
||||
dstpath = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_rename(srv, rb, path, dstpath);
|
||||
break;
|
||||
|
||||
case SSH_FXP_STAT:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_stat(srv, rb, path, true);
|
||||
break;
|
||||
|
||||
case SSH_FXP_LSTAT:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_stat(srv, rb, path, false);
|
||||
break;
|
||||
|
||||
case SSH_FXP_FSTAT:
|
||||
handle = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_fstat(srv, rb, handle);
|
||||
break;
|
||||
|
||||
case SSH_FXP_SETSTAT:
|
||||
path = get_string(req);
|
||||
get_fxp_attrs(req, &attrs);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_setstat(srv, rb, path, attrs);
|
||||
break;
|
||||
|
||||
case SSH_FXP_FSETSTAT:
|
||||
handle = get_string(req);
|
||||
get_fxp_attrs(req, &attrs);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_fsetstat(srv, rb, handle, attrs);
|
||||
break;
|
||||
|
||||
case SSH_FXP_READ:
|
||||
handle = get_string(req);
|
||||
offset = get_uint64(req);
|
||||
length = get_uint32(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_read(srv, rb, handle, offset, length);
|
||||
break;
|
||||
|
||||
case SSH_FXP_READDIR:
|
||||
handle = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_readdir(srv, rb, handle, INT_MAX, false);
|
||||
break;
|
||||
|
||||
case SSH_FXP_WRITE:
|
||||
handle = get_string(req);
|
||||
offset = get_uint64(req);
|
||||
data = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_write(srv, rb, handle, offset, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
fxp_reply_error(rb, SSH_FX_OP_UNSUPPORTED,
|
||||
"Unrecognised request type");
|
||||
break;
|
||||
|
||||
decode_error:
|
||||
fxp_reply_error(rb, SSH_FX_BAD_MESSAGE, "Unable to decode request");
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
static void default_reply_ok(SftpReplyBuilder *reply)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_STATUS;
|
||||
put_uint32(d->pkt, SSH_FX_OK);
|
||||
put_stringz(d->pkt, "");
|
||||
}
|
||||
|
||||
static void default_reply_error(
|
||||
SftpReplyBuilder *reply, unsigned code, const char *msg)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_STATUS;
|
||||
put_uint32(d->pkt, code);
|
||||
put_stringz(d->pkt, msg);
|
||||
}
|
||||
|
||||
static void default_reply_name_count(SftpReplyBuilder *reply, unsigned count)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_NAME;
|
||||
put_uint32(d->pkt, count);
|
||||
}
|
||||
|
||||
static void default_reply_full_name(SftpReplyBuilder *reply, ptrlen name,
|
||||
ptrlen longname, struct fxp_attrs attrs)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_NAME;
|
||||
put_stringpl(d->pkt, name);
|
||||
put_stringpl(d->pkt, longname);
|
||||
put_fxp_attrs(d->pkt, attrs);
|
||||
}
|
||||
|
||||
static void default_reply_simple_name(SftpReplyBuilder *reply, ptrlen name)
|
||||
{
|
||||
fxp_reply_name_count(reply, 1);
|
||||
fxp_reply_full_name(reply, name, PTRLEN_LITERAL(""), no_attrs);
|
||||
}
|
||||
|
||||
static void default_reply_handle(SftpReplyBuilder *reply, ptrlen handle)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_HANDLE;
|
||||
put_stringpl(d->pkt, handle);
|
||||
}
|
||||
|
||||
static void default_reply_data(SftpReplyBuilder *reply, ptrlen data)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_DATA;
|
||||
put_stringpl(d->pkt, data);
|
||||
}
|
||||
|
||||
static void default_reply_attrs(
|
||||
SftpReplyBuilder *reply, struct fxp_attrs attrs)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_ATTRS;
|
||||
put_fxp_attrs(d->pkt, attrs);
|
||||
}
|
||||
|
||||
const struct SftpReplyBuilderVtable DefaultSftpReplyBuilder_vt = {
|
||||
default_reply_ok,
|
||||
default_reply_error,
|
||||
default_reply_simple_name,
|
||||
default_reply_name_count,
|
||||
default_reply_full_name,
|
||||
default_reply_handle,
|
||||
default_reply_data,
|
||||
default_reply_attrs,
|
||||
};
|
||||
/*
|
||||
* Implement the centralised parts of the server side of SFTP.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "sftp.h"
|
||||
|
||||
struct sftp_packet *sftp_handle_request(
|
||||
SftpServer *srv, struct sftp_packet *req)
|
||||
{
|
||||
struct sftp_packet *reply;
|
||||
unsigned id;
|
||||
uint32_t flags;
|
||||
ptrlen path, dstpath, handle, data;
|
||||
uint64_t offset;
|
||||
unsigned length;
|
||||
struct fxp_attrs attrs;
|
||||
DefaultSftpReplyBuilder dsrb;
|
||||
SftpReplyBuilder *rb;
|
||||
|
||||
if (req->type == SSH_FXP_INIT) {
|
||||
/*
|
||||
* Special case which doesn't have a request id at the start.
|
||||
*/
|
||||
reply = sftp_pkt_init(SSH_FXP_VERSION);
|
||||
/*
|
||||
* Since we support only the lowest protocol version, we don't
|
||||
* need to take the min of this and the client's version, or
|
||||
* even to bother reading the client version number out of the
|
||||
* input packet.
|
||||
*/
|
||||
put_uint32(reply, SFTP_PROTO_VERSION);
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*
|
||||
* Centralise the request id handling. We'll overwrite the type
|
||||
* code of the output packet later.
|
||||
*/
|
||||
id = get_uint32(req);
|
||||
reply = sftp_pkt_init(0);
|
||||
put_uint32(reply, id);
|
||||
|
||||
dsrb.rb.vt = &DefaultSftpReplyBuilder_vt;
|
||||
dsrb.pkt = reply;
|
||||
rb = &dsrb.rb;
|
||||
|
||||
switch (req->type) {
|
||||
case SSH_FXP_REALPATH:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_realpath(srv, rb, path);
|
||||
break;
|
||||
|
||||
case SSH_FXP_OPEN:
|
||||
path = get_string(req);
|
||||
flags = get_uint32(req);
|
||||
get_fxp_attrs(req, &attrs);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
if ((flags & (SSH_FXF_READ|SSH_FXF_WRITE)) == 0) {
|
||||
fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
|
||||
"open without READ or WRITE flag");
|
||||
} else if ((flags & (SSH_FXF_CREAT|SSH_FXF_TRUNC)) == SSH_FXF_TRUNC) {
|
||||
fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
|
||||
"open with TRUNC but not CREAT");
|
||||
} else if ((flags & (SSH_FXF_CREAT|SSH_FXF_EXCL)) == SSH_FXF_EXCL) {
|
||||
fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
|
||||
"open with EXCL but not CREAT");
|
||||
} else {
|
||||
sftpsrv_open(srv, rb, path, flags, attrs);
|
||||
}
|
||||
break;
|
||||
|
||||
case SSH_FXP_OPENDIR:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_opendir(srv, rb, path);
|
||||
break;
|
||||
|
||||
case SSH_FXP_CLOSE:
|
||||
handle = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_close(srv, rb, handle);
|
||||
break;
|
||||
|
||||
case SSH_FXP_MKDIR:
|
||||
path = get_string(req);
|
||||
get_fxp_attrs(req, &attrs);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_mkdir(srv, rb, path, attrs);
|
||||
break;
|
||||
|
||||
case SSH_FXP_RMDIR:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_rmdir(srv, rb, path);
|
||||
break;
|
||||
|
||||
case SSH_FXP_REMOVE:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_remove(srv, rb, path);
|
||||
break;
|
||||
|
||||
case SSH_FXP_RENAME:
|
||||
path = get_string(req);
|
||||
dstpath = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_rename(srv, rb, path, dstpath);
|
||||
break;
|
||||
|
||||
case SSH_FXP_STAT:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_stat(srv, rb, path, true);
|
||||
break;
|
||||
|
||||
case SSH_FXP_LSTAT:
|
||||
path = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_stat(srv, rb, path, false);
|
||||
break;
|
||||
|
||||
case SSH_FXP_FSTAT:
|
||||
handle = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_fstat(srv, rb, handle);
|
||||
break;
|
||||
|
||||
case SSH_FXP_SETSTAT:
|
||||
path = get_string(req);
|
||||
get_fxp_attrs(req, &attrs);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_setstat(srv, rb, path, attrs);
|
||||
break;
|
||||
|
||||
case SSH_FXP_FSETSTAT:
|
||||
handle = get_string(req);
|
||||
get_fxp_attrs(req, &attrs);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_fsetstat(srv, rb, handle, attrs);
|
||||
break;
|
||||
|
||||
case SSH_FXP_READ:
|
||||
handle = get_string(req);
|
||||
offset = get_uint64(req);
|
||||
length = get_uint32(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_read(srv, rb, handle, offset, length);
|
||||
break;
|
||||
|
||||
case SSH_FXP_READDIR:
|
||||
handle = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_readdir(srv, rb, handle, INT_MAX, false);
|
||||
break;
|
||||
|
||||
case SSH_FXP_WRITE:
|
||||
handle = get_string(req);
|
||||
offset = get_uint64(req);
|
||||
data = get_string(req);
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
sftpsrv_write(srv, rb, handle, offset, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (get_err(req))
|
||||
goto decode_error;
|
||||
fxp_reply_error(rb, SSH_FX_OP_UNSUPPORTED,
|
||||
"Unrecognised request type");
|
||||
break;
|
||||
|
||||
decode_error:
|
||||
fxp_reply_error(rb, SSH_FX_BAD_MESSAGE, "Unable to decode request");
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
static void default_reply_ok(SftpReplyBuilder *reply)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_STATUS;
|
||||
put_uint32(d->pkt, SSH_FX_OK);
|
||||
put_stringz(d->pkt, "");
|
||||
}
|
||||
|
||||
static void default_reply_error(
|
||||
SftpReplyBuilder *reply, unsigned code, const char *msg)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_STATUS;
|
||||
put_uint32(d->pkt, code);
|
||||
put_stringz(d->pkt, msg);
|
||||
}
|
||||
|
||||
static void default_reply_name_count(SftpReplyBuilder *reply, unsigned count)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_NAME;
|
||||
put_uint32(d->pkt, count);
|
||||
}
|
||||
|
||||
static void default_reply_full_name(SftpReplyBuilder *reply, ptrlen name,
|
||||
ptrlen longname, struct fxp_attrs attrs)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_NAME;
|
||||
put_stringpl(d->pkt, name);
|
||||
put_stringpl(d->pkt, longname);
|
||||
put_fxp_attrs(d->pkt, attrs);
|
||||
}
|
||||
|
||||
static void default_reply_simple_name(SftpReplyBuilder *reply, ptrlen name)
|
||||
{
|
||||
fxp_reply_name_count(reply, 1);
|
||||
fxp_reply_full_name(reply, name, PTRLEN_LITERAL(""), no_attrs);
|
||||
}
|
||||
|
||||
static void default_reply_handle(SftpReplyBuilder *reply, ptrlen handle)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_HANDLE;
|
||||
put_stringpl(d->pkt, handle);
|
||||
}
|
||||
|
||||
static void default_reply_data(SftpReplyBuilder *reply, ptrlen data)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_DATA;
|
||||
put_stringpl(d->pkt, data);
|
||||
}
|
||||
|
||||
static void default_reply_attrs(
|
||||
SftpReplyBuilder *reply, struct fxp_attrs attrs)
|
||||
{
|
||||
DefaultSftpReplyBuilder *d =
|
||||
container_of(reply, DefaultSftpReplyBuilder, rb);
|
||||
d->pkt->type = SSH_FXP_ATTRS;
|
||||
put_fxp_attrs(d->pkt, attrs);
|
||||
}
|
||||
|
||||
const struct SftpReplyBuilderVtable DefaultSftpReplyBuilder_vt = {
|
||||
default_reply_ok,
|
||||
default_reply_error,
|
||||
default_reply_simple_name,
|
||||
default_reply_name_count,
|
||||
default_reply_full_name,
|
||||
default_reply_handle,
|
||||
default_reply_data,
|
||||
default_reply_attrs,
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user