Compare commits

...
41 Commits
Author SHA1 Message Date
cyd01 1c3398cbb7 Tagging the 0.74.3.4 release of the kitty project. 2020-12-02 19:46:26 +01:00
cyd01 2d0a377626 Tagging the 0.74.3.3 release of the kitty project. 2020-12-01 18:43:53 +01:00
cyd01 232c3b29fd Tagging the 0.74.3.2 release of the kitty project. 2020-11-30 19:33:29 +01:00
cyd01 3bab5a6151 New Github workflow 2020-11-15 12:38:37 +01:00
CydandGitHub 9bb4b61196 Merge pull request #248 from jaynemo/cygwindocupdate
Streamline instructions, changes to quoting to ensure commands run
2020-11-15 12:33:47 +01:00
jaynemo 80597e340d Streamline instructions, changes to quoting to ensure commands run
correctly.
2020-11-07 10:20:55 +13:00
cyd01 c558736424 Tagging the 0.74.3.1 release of the kitty project. 2020-11-06 18:37:06 +01:00
cyd01 7f7fac22e9 Tagging the 0.74.2.8 release of the kitty project. 2020-11-03 13:26:02 +01:00
cyd01 8124638aba Tagging the 0.74.2.7 release of the kitty project. 2020-10-29 08:40:58 +01:00
cyd01 63053a885f New funding 2020-10-11 19:49:07 +02:00
cyd01 464c88d396 Tagging the 0.74.2.6 release of the kitty project. 2020-10-10 23:57:13 +02:00
cyd01 6af39c95c2 Tagging the 0.74.2.5 release of the kitty project. 2020-10-09 19:18:36 +02:00
cyd01 ebb5e2f164 Tagging the 0.74.2.4 release of the kitty project. 2020-10-05 07:55:27 +02:00
cyd01 c4b6dabb28 Add 64 bits tools to Github Actions 2020-09-27 12:12:48 +02:00
CydandGitHub e85550c70e Update README.md 2020-09-26 23:34:05 +02:00
cyd01 9d80c6b171 Website update 2020-09-26 23:21:52 +02:00
CydandGitHub 9a8af62267 Update README.md 2020-09-26 23:18:45 +02:00
CydandGitHub 45c65bbdb0 Update README.md 2020-09-26 23:18:18 +02:00
CydandGitHub 12d8132e56 Update README.md 2020-09-26 23:17:52 +02:00
CydandGitHub b76939b3d0 Update README.md 2020-09-26 23:16:31 +02:00
cyd01 659bc96519 Tagging the 0.74.2.3 release of the kitty project. 2020-09-26 22:54:48 +02:00
cyd01 cb833b241a Add Website source code 2020-09-26 22:13:26 +02:00
cyd01 042b9f5cef Tagging the 0.74.2.2 release of the kitty project. 2020-09-23 10:57:01 +02:00
cyd01 1d13ae9175 Change github actions 2020-09-20 12:22:45 +02:00
cyd01 2e87a6dc2b Change github actions 2020-09-20 12:18:09 +02:00
cyd01 057faa84f9 Merge branch 'master' of github.com:cyd01/KiTTY 2020-09-20 12:11:37 +02:00
cyd01 8612fa5ef2 Change github actions 2020-09-20 12:11:17 +02:00
CydandGitHub 0ae14d0859 Update README.md 2020-09-19 14:50:29 +02:00
cyd01 977da5f49c Tagging the 0.74.2.1 release of the kitty project. 2020-09-18 18:05:42 +02:00
cyd01 cc43da0078 Tagging the 0.74.1.1 release of the kitty project. 2020-09-16 22:14:35 +02:00
cyd01 7400667711 Tagging the 0.74.0.7 release of the kitty project. 2020-09-12 22:52:32 +02:00
cyd01 b9f427b176 Tagging the 0.74.0.6 release of the kitty project. 2020-09-02 10:45:29 +02:00
cyd01 f1377e7040 Tagging the 0.74.0.5 release of the kitty project. 2020-08-24 18:21:15 +02:00
cyd01 62c2de1dd2 Tagging the 0.74.0.4 release of the kitty project. 2020-08-12 09:02:00 +02:00
cyd01 5180166b5d Tagging the 0.74.0.3 release of the kitty project. 2020-08-06 19:18:31 +02:00
cyd01 e45c98b427 New current folder for portablemode definition (shared with pscp, plink ...) 2020-08-03 09:42:00 +02:00
cyd01 dea7220469 New comment 2020-08-03 08:58:49 +02:00
cyd01 49d45ac099 Change main version for Github Actions 2020-07-09 19:33:23 +02:00
cyd01 a38fa17431 Tagging the 0.74.0.2 release of the kitty project. 2020-07-02 23:04:43 +02:00
cyd01 4c9cecbe68 Tagging the 0.74.0.1 release of the kitty project. 2020-07-02 21:31:58 +02:00
cyd01 8312a7eece Tagging the 0.73.2.18 release of the kitty project. 2020-06-23 18:38:39 +02:00
456 changed files with 39038 additions and 21872 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
github: [ cyd01 ]
custom: [ "https://www.paypal.com/donate/?token=oyJhWJ4Z0AW-wJxIBGIfxC2LyYmnbSPU2Vax9EJdVAtaEYvh8VXsSyCOHCVCO8Y9uH_I4G&country.x=GB&locale.x=GB", "http://kitty.9bis.com" ]
custom: [ "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=65VYL8F5AD57G&source=url", "https://www.9bis.com/kitty/" ]
+10 -12
View File
@@ -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
-5
View File
@@ -1,5 +0,0 @@
#define RELEASE 0.73
#define TEXTVER "Release 0.73"
#define SSHVER "-Release-0.73"
#define BINARY_VERSION 0,73,2,17
#define SOURCE_COMMIT "unavailable"
-1
View File
@@ -1 +0,0 @@
"0.73.2"
-1
View File
@@ -1 +0,0 @@
17
-450
View File
@@ -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 ;
}
@@ -1011,11 +1019,16 @@ void filter_session_portable(union control *ctrl, dlgparam *dlg, const int nb, c
// 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
@@ -1026,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);
@@ -1045,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) {
@@ -1052,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 ) ;
int j;
if( get_param("INIFILE")==SAVEMODE_DIR ) {
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
@@ -1069,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
@@ -1083,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;
@@ -1133,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) ) ;
}
@@ -1182,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) ) ;
}
@@ -1194,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) ) ;
}
@@ -1256,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) ) ;
}
@@ -1300,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) ) {
@@ -1857,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);
@@ -1988,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)) {
@@ -2155,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 {
@@ -2182,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++)
@@ -2197,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) {
@@ -2258,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;
@@ -2348,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,
@@ -2374,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
@@ -2449,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),
@@ -2919,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.
@@ -3027,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',
@@ -3336,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),
@@ -3656,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));
}
/*
@@ -4041,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),
@@ -4081,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
@@ -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);
}
+380 -389
View File
@@ -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");
}
+405 -420
View File
@@ -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
+111 -111
View File
@@ -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
+26 -18
View File
@@ -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
+30 -15
View File
@@ -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) {
+16 -24
View File
@@ -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) \
@@ -1600,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 */ \
@@ -1895,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);
/*
@@ -2118,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.
@@ -2156,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.
+428 -426
View File
@@ -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);
@@ -25,12 +25,13 @@
#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. */
@@ -510,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);
}
@@ -619,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));
@@ -839,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) );
@@ -890,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) );
@@ -910,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
@@ -950,19 +947,27 @@ bool load_settings(const char *section, Conf *conf)
close_settings_r(sesskey);
#ifdef MOD_PERSO
if (exists && conf_launchable(conf)) {
if( get_param("INIFILE") == SAVEMODE_DIR ) {
char *name;
if( strlen(conf_get_str( conf, CONF_folder))>0 ) {
name=(char*)malloc( strlen(section)+strlen(conf_get_str( conf, CONF_folder))+2);
sprintf(name,"%s/%s",conf_get_str( conf, CONF_folder),section);
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 {
name=(char*)malloc( strlen(section)+1);
sprintf(name,"%s",section);
add_session_to_jumplist(section);
}
add_session_to_jumplist(name);
free(name);
} else
add_session_to_jumplist(section);
}
#else
if (exists && conf_launchable(conf))
@@ -1176,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);
@@ -1253,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);
@@ -1503,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 );
@@ -1548,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 );
@@ -1569,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));
}
@@ -1700,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