Preparing version 0.77
This commit is contained in:
@@ -3726,11 +3726,34 @@ static void do_osc(Terminal *term)
|
||||
// ok, let us try to answer something
|
||||
|
||||
// base64-encode
|
||||
// result in null-terminated char* out
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
|
||||
char* out = malloc(reply_size*2);
|
||||
int count = base64_encode_block((char*)reply, reply_size, out, &_state);
|
||||
#ifdef MOD_PERSO
|
||||
count += base64_encode_blockend(out + count, &_state);
|
||||
#else
|
||||
// finishing '=' characters
|
||||
char* next_char = out + count;
|
||||
switch (_state.step)
|
||||
{
|
||||
case step_B:
|
||||
*next_char++ = base64_encode_value(_state.result);
|
||||
*next_char++ = '=';
|
||||
*next_char++ = '=';
|
||||
break;
|
||||
case step_C:
|
||||
*next_char++ = base64_encode_value(_state.result);
|
||||
*next_char++ = '=';
|
||||
break;
|
||||
case step_A:
|
||||
break;
|
||||
}
|
||||
count = next_char - out;
|
||||
out[count] = 0;
|
||||
#endif
|
||||
|
||||
// send escape seq
|
||||
|
||||
|
||||
@@ -5696,11 +5696,33 @@ if( (GetKeyState(VK_MENU)&0x8000) && (wParam==VK_SPACE) ) {
|
||||
memcpy(kev + 14, &type, sizeof(type));
|
||||
|
||||
// base64-encode kev
|
||||
// result in null-terminated char* out
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
char* out = malloc(15*2);
|
||||
int count = base64_encode_block(kev, 15, out, &_state);
|
||||
#ifdef MOD_PERSO
|
||||
count += base64_encode_blockend(out + count, &_state);
|
||||
#else
|
||||
// finishing '=' characters
|
||||
char* next_char = out + count;
|
||||
switch (_state.step)
|
||||
{
|
||||
case step_B:
|
||||
*next_char++ = base64_encode_value(_state.result);
|
||||
*next_char++ = '=';
|
||||
*next_char++ = '=';
|
||||
break;
|
||||
case step_C:
|
||||
*next_char++ = base64_encode_value(_state.result);
|
||||
*next_char++ = '=';
|
||||
break;
|
||||
case step_A:
|
||||
break;
|
||||
}
|
||||
count = next_char - out;
|
||||
out[count] = 0;
|
||||
#endif
|
||||
|
||||
// send escape seq
|
||||
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* SSH agent forwarding.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "pageant.h"
|
||||
#include "sshchan.h"
|
||||
|
||||
typedef struct agentf {
|
||||
SshChannel *c;
|
||||
bufchain inbuffer;
|
||||
agent_pending_query *pending;
|
||||
bool input_wanted;
|
||||
bool rcvd_eof;
|
||||
|
||||
Channel chan;
|
||||
} agentf;
|
||||
|
||||
static void agentf_got_response(agentf *af, void *reply, int replylen)
|
||||
{
|
||||
af->pending = NULL;
|
||||
|
||||
if (!reply) {
|
||||
/* The real agent didn't send any kind of reply at all for
|
||||
* some reason, so fake an SSH_AGENT_FAILURE. */
|
||||
reply = "\0\0\0\1\5";
|
||||
replylen = 5;
|
||||
}
|
||||
|
||||
sshfwd_write(af->c, reply, replylen);
|
||||
}
|
||||
|
||||
static void agentf_callback(void *vctx, void *reply, int replylen);
|
||||
|
||||
static void agentf_try_forward(agentf *af)
|
||||
{
|
||||
size_t datalen, length;
|
||||
strbuf *message;
|
||||
unsigned char msglen[4];
|
||||
void *reply;
|
||||
int replylen;
|
||||
|
||||
/*
|
||||
* Don't try to parallelise agent requests. Wait for each one to
|
||||
* return before attempting the next.
|
||||
*/
|
||||
if (af->pending)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the outgoing side of the channel connection is currently
|
||||
* throttled, don't submit any new forwarded requests to the real
|
||||
* agent. This causes the input side of the agent forwarding not
|
||||
* to be emptied, exerting the required back-pressure on the
|
||||
* remote client, and encouraging it to read our responses before
|
||||
* sending too many more requests.
|
||||
*/
|
||||
if (!af->input_wanted)
|
||||
return;
|
||||
|
||||
while (1) {
|
||||
/*
|
||||
* Try to extract a complete message from the input buffer.
|
||||
*/
|
||||
datalen = bufchain_size(&af->inbuffer);
|
||||
if (datalen < 4)
|
||||
break; /* not even a length field available yet */
|
||||
|
||||
bufchain_fetch(&af->inbuffer, msglen, 4);
|
||||
length = GET_32BIT_MSB_FIRST(msglen);
|
||||
|
||||
if (length > AGENT_MAX_MSGLEN-4) {
|
||||
/*
|
||||
* If the remote has sent a message that's just _too_
|
||||
* long, we should reject it in advance of seeing the rest
|
||||
* of the incoming message, and also close the connection
|
||||
* for good measure (which avoids us having to faff about
|
||||
* with carefully ignoring just the right number of bytes
|
||||
* from the overlong message).
|
||||
*/
|
||||
agentf_got_response(af, NULL, 0);
|
||||
sshfwd_write_eof(af->c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length > datalen - 4)
|
||||
break; /* a whole message is not yet available */
|
||||
|
||||
bufchain_consume(&af->inbuffer, 4);
|
||||
|
||||
message = strbuf_new_for_agent_query();
|
||||
bufchain_fetch_consume(
|
||||
&af->inbuffer, strbuf_append(message, length), length);
|
||||
af->pending = agent_query(
|
||||
message, &reply, &replylen, agentf_callback, af);
|
||||
strbuf_free(message);
|
||||
|
||||
if (af->pending)
|
||||
return; /* agent_query promised to reply in due course */
|
||||
|
||||
/*
|
||||
* If the agent gave us an answer immediately, pass it
|
||||
* straight on and go round this loop again.
|
||||
*/
|
||||
agentf_got_response(af, reply, replylen);
|
||||
sfree(reply);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we get here (i.e. we left the above while loop via 'break'
|
||||
* rather than 'return'), that means we've determined that the
|
||||
* input buffer for the agent forwarding connection doesn't
|
||||
* contain a complete request.
|
||||
*
|
||||
* So if there's potentially more data to come, we can return now,
|
||||
* and wait for the remote client to send it. But if the remote
|
||||
* has sent EOF, it would be a mistake to do that, because we'd be
|
||||
* waiting a long time. So this is the moment to check for EOF,
|
||||
* and respond appropriately.
|
||||
*/
|
||||
if (af->rcvd_eof)
|
||||
sshfwd_write_eof(af->c);
|
||||
}
|
||||
|
||||
static void agentf_callback(void *vctx, void *reply, int replylen)
|
||||
{
|
||||
agentf *af = (agentf *)vctx;
|
||||
|
||||
agentf_got_response(af, reply, replylen);
|
||||
sfree(reply);
|
||||
|
||||
/*
|
||||
* Now try to extract and send further messages from the channel's
|
||||
* input-side buffer.
|
||||
*/
|
||||
agentf_try_forward(af);
|
||||
}
|
||||
|
||||
static void agentf_free(Channel *chan);
|
||||
static size_t agentf_send(Channel *chan, bool is_stderr, const void *, size_t);
|
||||
static void agentf_send_eof(Channel *chan);
|
||||
static char *agentf_log_close_msg(Channel *chan);
|
||||
static void agentf_set_input_wanted(Channel *chan, bool wanted);
|
||||
|
||||
static const ChannelVtable agentf_channelvt = {
|
||||
.free = agentf_free,
|
||||
.open_confirmation = chan_remotely_opened_confirmation,
|
||||
.open_failed = chan_remotely_opened_failure,
|
||||
.send = agentf_send,
|
||||
.send_eof = agentf_send_eof,
|
||||
.set_input_wanted = agentf_set_input_wanted,
|
||||
.log_close_msg = agentf_log_close_msg,
|
||||
.want_close = chan_default_want_close,
|
||||
.rcvd_exit_status = chan_no_exit_status,
|
||||
.rcvd_exit_signal = chan_no_exit_signal,
|
||||
.rcvd_exit_signal_numeric = chan_no_exit_signal_numeric,
|
||||
.run_shell = chan_no_run_shell,
|
||||
.run_command = chan_no_run_command,
|
||||
.run_subsystem = chan_no_run_subsystem,
|
||||
.enable_x11_forwarding = chan_no_enable_x11_forwarding,
|
||||
.enable_agent_forwarding = chan_no_enable_agent_forwarding,
|
||||
.allocate_pty = chan_no_allocate_pty,
|
||||
.set_env = chan_no_set_env,
|
||||
.send_break = chan_no_send_break,
|
||||
.send_signal = chan_no_send_signal,
|
||||
.change_window_size = chan_no_change_window_size,
|
||||
.request_response = chan_no_request_response,
|
||||
};
|
||||
|
||||
Channel *agentf_new(SshChannel *c)
|
||||
{
|
||||
agentf *af = snew(agentf);
|
||||
af->c = c;
|
||||
af->chan.vt = &agentf_channelvt;
|
||||
af->chan.initial_fixed_window_size = 0;
|
||||
af->rcvd_eof = false;
|
||||
bufchain_init(&af->inbuffer);
|
||||
af->pending = NULL;
|
||||
af->input_wanted = true;
|
||||
return &af->chan;
|
||||
}
|
||||
|
||||
static void agentf_free(Channel *chan)
|
||||
{
|
||||
assert(chan->vt == &agentf_channelvt);
|
||||
agentf *af = container_of(chan, agentf, chan);
|
||||
|
||||
if (af->pending)
|
||||
agent_cancel_query(af->pending);
|
||||
bufchain_clear(&af->inbuffer);
|
||||
sfree(af);
|
||||
}
|
||||
|
||||
static size_t agentf_send(Channel *chan, bool is_stderr,
|
||||
const void *data, size_t length)
|
||||
{
|
||||
assert(chan->vt == &agentf_channelvt);
|
||||
agentf *af = container_of(chan, agentf, chan);
|
||||
bufchain_add(&af->inbuffer, data, length);
|
||||
agentf_try_forward(af);
|
||||
|
||||
/*
|
||||
* We exert back-pressure on an agent forwarding client if and
|
||||
* only if we're waiting for the response to an asynchronous agent
|
||||
* request. This prevents the client running out of window while
|
||||
* receiving the _first_ message, but means that if any message
|
||||
* takes time to process, the client will be discouraged from
|
||||
* sending an endless stream of further ones after it.
|
||||
*/
|
||||
return (af->pending ? bufchain_size(&af->inbuffer) : 0);
|
||||
}
|
||||
|
||||
static void agentf_send_eof(Channel *chan)
|
||||
{
|
||||
assert(chan->vt == &agentf_channelvt);
|
||||
agentf *af = container_of(chan, agentf, chan);
|
||||
|
||||
af->rcvd_eof = true;
|
||||
|
||||
/* Call try_forward, which will respond to the EOF now if
|
||||
* appropriate, or wait until the queue of outstanding requests is
|
||||
* dealt with if not. */
|
||||
agentf_try_forward(af);
|
||||
}
|
||||
|
||||
static char *agentf_log_close_msg(Channel *chan)
|
||||
{
|
||||
return dupstr("Agent-forwarding connection closed");
|
||||
}
|
||||
|
||||
static void agentf_set_input_wanted(Channel *chan, bool wanted)
|
||||
{
|
||||
assert(chan->vt == &agentf_channelvt);
|
||||
agentf *af = container_of(chan, agentf, chan);
|
||||
|
||||
af->input_wanted = wanted;
|
||||
|
||||
/* Agent forwarding channels are buffer-managed by not asking the
|
||||
* agent questions if the SSH channel isn't accepting input. So if
|
||||
* it's started again, we should ask a question if we have one
|
||||
* pending.. */
|
||||
if (wanted)
|
||||
agentf_try_forward(af);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* aqsync.c: the agent_query_synchronous() wrapper function.
|
||||
*
|
||||
* This is a very small thing to have to put in its own module, but it
|
||||
* wants to be shared between back ends, and exist in any SSH client
|
||||
* program and also Pageant, and _nowhere else_ (because it pulls in
|
||||
* the main agent_query).
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
void agent_query_synchronous(strbuf *query, void **out, int *outlen)
|
||||
{
|
||||
agent_pending_query *pending;
|
||||
|
||||
pending = agent_query(query, out, outlen, NULL, 0);
|
||||
assert(!pending);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Linking module for PuTTY proper: list the available backends
|
||||
* including ssh.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "putty.h"
|
||||
|
||||
/*
|
||||
* This appname is not strictly in the right place, since Plink
|
||||
* also uses this module. However, Plink doesn't currently use any
|
||||
* of the dialog-box sorts of things that make use of appname, so
|
||||
* it shouldn't do any harm here. I'm trying to avoid having to
|
||||
* have tiny little source modules containing nothing but
|
||||
* declarations of appname, for as long as I can...
|
||||
*/
|
||||
const char *const appname = "PuTTY";
|
||||
|
||||
const int be_default_protocol = PROT_SSH;
|
||||
|
||||
const struct BackendVtable *const backends[] = {
|
||||
&ssh_backend,
|
||||
&telnet_backend,
|
||||
&rlogin_backend,
|
||||
&supdup_backend,
|
||||
&raw_backend,
|
||||
&sshconn_backend,
|
||||
#ifdef MOD_ADB
|
||||
&adb_backend,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
const size_t n_ui_backends = 1;
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Linking module for PuTTY proper: list the available backends
|
||||
* including ssh, plus the serial backend.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "putty.h"
|
||||
|
||||
/*
|
||||
* This appname is not strictly in the right place, since Plink
|
||||
* also uses this module. However, Plink doesn't currently use any
|
||||
* of the dialog-box sorts of things that make use of appname, so
|
||||
* it shouldn't do any harm here. I'm trying to avoid having to
|
||||
* have tiny little source modules containing nothing but
|
||||
* declarations of appname, for as long as I can...
|
||||
*/
|
||||
#if (defined MOD_PERSO) && (!defined FLJ)
|
||||
char *appname = "KiTTY";
|
||||
#else
|
||||
const char *const appname = "PuTTY";
|
||||
#endif
|
||||
|
||||
const int be_default_protocol = PROT_SSH;
|
||||
|
||||
const struct BackendVtable *const backends[] = {
|
||||
&ssh_backend,
|
||||
&serial_backend,
|
||||
&telnet_backend,
|
||||
&rlogin_backend,
|
||||
&supdup_backend,
|
||||
&raw_backend,
|
||||
#ifdef MOD_ADB
|
||||
&adb_backend,
|
||||
#endif
|
||||
&sshconn_backend,
|
||||
NULL
|
||||
};
|
||||
|
||||
const size_t n_ui_backends = 2;
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* be_misc.c: helper functions shared between main network backends.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "network.h"
|
||||
|
||||
void backend_socket_log(Seat *seat, LogContext *logctx,
|
||||
PlugLogType type, SockAddr *addr, int port,
|
||||
const char *error_msg, int error_code, Conf *conf,
|
||||
bool session_started)
|
||||
{
|
||||
char addrbuf[256], *msg;
|
||||
|
||||
switch (type) {
|
||||
case PLUGLOG_CONNECT_TRYING:
|
||||
sk_getaddr(addr, addrbuf, lenof(addrbuf));
|
||||
if (sk_addr_needs_port(addr)) {
|
||||
msg = dupprintf("Connecting to %s port %d", addrbuf, port);
|
||||
} else {
|
||||
msg = dupprintf("Connecting to %s", addrbuf);
|
||||
}
|
||||
break;
|
||||
case PLUGLOG_CONNECT_FAILED:
|
||||
sk_getaddr(addr, addrbuf, lenof(addrbuf));
|
||||
msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
|
||||
break;
|
||||
case PLUGLOG_CONNECT_SUCCESS:
|
||||
sk_getaddr(addr, addrbuf, lenof(addrbuf));
|
||||
msg = dupprintf("Connected to %s", addrbuf);
|
||||
break;
|
||||
case PLUGLOG_PROXY_MSG: {
|
||||
/* Proxy-related log messages have their own identifying
|
||||
* prefix already, put on by our caller. */
|
||||
int len, log_to_term;
|
||||
|
||||
/* Suffix \r\n temporarily, so we can log to the terminal. */
|
||||
msg = dupprintf("%s\r\n", error_msg);
|
||||
len = strlen(msg);
|
||||
assert(len >= 2);
|
||||
|
||||
log_to_term = conf_get_int(conf, CONF_proxy_log_to_term);
|
||||
if (log_to_term == AUTO)
|
||||
log_to_term = session_started ? FORCE_OFF : FORCE_ON;
|
||||
if (log_to_term == FORCE_ON)
|
||||
seat_stderr(seat, msg, len);
|
||||
|
||||
msg[len-2] = '\0'; /* remove the \r\n again */
|
||||
break;
|
||||
}
|
||||
default:
|
||||
msg = NULL; /* shouldn't happen, but placate optimiser */
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg) {
|
||||
logevent(logctx, msg);
|
||||
sfree(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void psb_init(ProxyStderrBuf *psb)
|
||||
{
|
||||
psb->size = 0;
|
||||
}
|
||||
|
||||
void log_proxy_stderr(Plug *plug, ProxyStderrBuf *psb,
|
||||
const void *vdata, size_t len)
|
||||
{
|
||||
const char *data = (const char *)vdata;
|
||||
|
||||
/*
|
||||
* This helper function allows us to collect the data written to a
|
||||
* local proxy command's standard error in whatever size chunks we
|
||||
* happen to get from its pipe, and whenever we have a complete
|
||||
* line, we pass it to plug_log.
|
||||
*
|
||||
* (We also do this when the buffer in psb fills up, to avoid just
|
||||
* allocating more and more memory forever, and also to keep Event
|
||||
* Log lines reasonably bounded in size.)
|
||||
*
|
||||
* Prerequisites: a plug to log to, and a ProxyStderrBuf stored
|
||||
* somewhere to collect any not-yet-output partial line.
|
||||
*/
|
||||
|
||||
while (len > 0) {
|
||||
/*
|
||||
* Copy as much data into psb->buf as will fit.
|
||||
*/
|
||||
assert(psb->size < lenof(psb->buf));
|
||||
size_t to_consume = lenof(psb->buf) - psb->size;
|
||||
if (to_consume > len)
|
||||
to_consume = len;
|
||||
memcpy(psb->buf + psb->size, data, to_consume);
|
||||
data += to_consume;
|
||||
len -= to_consume;
|
||||
psb->size += to_consume;
|
||||
|
||||
/*
|
||||
* Output any full lines in psb->buf.
|
||||
*/
|
||||
size_t pos = 0;
|
||||
while (pos < psb->size) {
|
||||
char *nlpos = memchr(psb->buf + pos, '\n', psb->size - pos);
|
||||
if (!nlpos)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Found a newline in the buffer, so we can output a line.
|
||||
*/
|
||||
size_t endpos = nlpos - psb->buf;
|
||||
while (endpos > pos && (psb->buf[endpos-1] == '\n' ||
|
||||
psb->buf[endpos-1] == '\r'))
|
||||
endpos--;
|
||||
char *msg = dupprintf(
|
||||
"proxy: %.*s", (int)(endpos - pos), psb->buf + pos);
|
||||
plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0);
|
||||
sfree(msg);
|
||||
|
||||
pos = nlpos - psb->buf + 1;
|
||||
assert(pos <= psb->size);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the buffer is completely full and we didn't output
|
||||
* anything, then output the whole thing, flagging it as a
|
||||
* truncated line.
|
||||
*/
|
||||
if (pos == 0 && psb->size == lenof(psb->buf)) {
|
||||
char *msg = dupprintf(
|
||||
"proxy (partial line): %.*s", (int)psb->size, psb->buf);
|
||||
plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0);
|
||||
sfree(msg);
|
||||
|
||||
pos = psb->size = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now move any remaining data up to the front of the buffer.
|
||||
*/
|
||||
size_t newsize = psb->size - pos;
|
||||
if (newsize)
|
||||
memmove(psb->buf, psb->buf + pos, newsize);
|
||||
psb->size = newsize;
|
||||
|
||||
/*
|
||||
* And loop round again if there's more data to be read from
|
||||
* our input.
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Linking module for programs that do not support selection of backend
|
||||
* (such as pterm).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "putty.h"
|
||||
|
||||
const int be_default_protocol = -1;
|
||||
|
||||
const struct BackendVtable *const backends[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
const size_t n_ui_backends = 0;
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Linking module for PuTTYtel: list the available backends not
|
||||
* including ssh.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "putty.h"
|
||||
|
||||
const int be_default_protocol = PROT_TELNET;
|
||||
|
||||
const char *const appname = "PuTTYtel";
|
||||
|
||||
const struct BackendVtable *const backends[] = {
|
||||
&telnet_backend,
|
||||
&serial_backend,
|
||||
&rlogin_backend,
|
||||
&supdup_backend,
|
||||
&raw_backend,
|
||||
#ifdef MOD_ADB
|
||||
&adb_backend,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
const size_t n_ui_backends = 2;
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Linking module for PuTTYtel: list the available backends not
|
||||
* including ssh.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "putty.h"
|
||||
|
||||
const int be_default_protocol = PROT_TELNET;
|
||||
|
||||
const char *const appname = "PuTTYtel";
|
||||
|
||||
const struct BackendVtable *const backends[] = {
|
||||
&telnet_backend,
|
||||
&rlogin_backend,
|
||||
&supdup_backend,
|
||||
&raw_backend,
|
||||
#ifdef MOD_ADB
|
||||
&adb_backend,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
const size_t n_ui_backends = 1;
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Linking module for programs that are restricted to only using
|
||||
* SSH-type protocols (pscp and psftp). These still have a choice of
|
||||
* two actual backends, because they can also speak PROT_SSHCONN.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "putty.h"
|
||||
|
||||
const int be_default_protocol = PROT_SSH;
|
||||
|
||||
const struct BackendVtable *const backends[] = {
|
||||
&ssh_backend,
|
||||
&sshconn_backend,
|
||||
NULL
|
||||
};
|
||||
|
||||
const size_t n_ui_backends = 0; /* not used in programs with a config UI */
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Facility for queueing callback functions to be run from the
|
||||
* top-level event loop after the current top-level activity finishes.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
struct callback {
|
||||
struct callback *next;
|
||||
|
||||
toplevel_callback_fn_t fn;
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
static struct callback *cbcurr = NULL, *cbhead = NULL, *cbtail = NULL;
|
||||
|
||||
static toplevel_callback_notify_fn_t notify_frontend = NULL;
|
||||
static void *notify_ctx = NULL;
|
||||
|
||||
void request_callback_notifications(toplevel_callback_notify_fn_t fn,
|
||||
void *ctx)
|
||||
{
|
||||
notify_frontend = fn;
|
||||
notify_ctx = ctx;
|
||||
}
|
||||
|
||||
static void run_idempotent_callback(void *ctx)
|
||||
{
|
||||
struct IdempotentCallback *ic = (struct IdempotentCallback *)ctx;
|
||||
ic->queued = false;
|
||||
ic->fn(ic->ctx);
|
||||
}
|
||||
|
||||
void queue_idempotent_callback(struct IdempotentCallback *ic)
|
||||
{
|
||||
if (ic->queued)
|
||||
return;
|
||||
ic->queued = true;
|
||||
queue_toplevel_callback(run_idempotent_callback, ic);
|
||||
}
|
||||
|
||||
void delete_callbacks_for_context(void *ctx)
|
||||
{
|
||||
struct callback *newhead, *newtail;
|
||||
|
||||
newhead = newtail = NULL;
|
||||
while (cbhead) {
|
||||
struct callback *cb = cbhead;
|
||||
cbhead = cbhead->next;
|
||||
if (cb->ctx == ctx ||
|
||||
(cb->fn == run_idempotent_callback &&
|
||||
((struct IdempotentCallback *)cb->ctx)->ctx == ctx)) {
|
||||
sfree(cb);
|
||||
} else {
|
||||
if (!newhead)
|
||||
newhead = cb;
|
||||
else
|
||||
newtail->next = cb;
|
||||
|
||||
newtail = cb;
|
||||
}
|
||||
}
|
||||
|
||||
cbhead = newhead;
|
||||
cbtail = newtail;
|
||||
if (newtail)
|
||||
newtail->next = NULL;
|
||||
}
|
||||
|
||||
void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
|
||||
{
|
||||
struct callback *cb;
|
||||
|
||||
cb = snew(struct callback);
|
||||
cb->fn = fn;
|
||||
cb->ctx = ctx;
|
||||
|
||||
/*
|
||||
* If the front end has requested notification of pending
|
||||
* callbacks, and we didn't already have one queued, let it know
|
||||
* we do have one now.
|
||||
*
|
||||
* If cbcurr is non-NULL, i.e. we are actually in the middle of
|
||||
* executing a callback right now, then we count that as the queue
|
||||
* already having been non-empty. That saves the front end getting
|
||||
* a constant stream of needless re-notifications if the last
|
||||
* callback keeps re-scheduling itself.
|
||||
*/
|
||||
if (notify_frontend && !cbhead && !cbcurr)
|
||||
notify_frontend(notify_ctx);
|
||||
|
||||
if (cbtail)
|
||||
cbtail->next = cb;
|
||||
else
|
||||
cbhead = cb;
|
||||
cbtail = cb;
|
||||
cb->next = NULL;
|
||||
}
|
||||
|
||||
bool run_toplevel_callbacks(void)
|
||||
{
|
||||
bool done_something = false;
|
||||
|
||||
if (cbhead) {
|
||||
/*
|
||||
* Transfer the head callback into cbcurr to indicate that
|
||||
* it's being executed. Then operations which transform the
|
||||
* queue, like delete_callbacks_for_context, can proceed as if
|
||||
* it's not there.
|
||||
*/
|
||||
cbcurr = cbhead;
|
||||
cbhead = cbhead->next;
|
||||
if (!cbhead)
|
||||
cbtail = NULL;
|
||||
|
||||
/*
|
||||
* Now run the callback, and then clear it out of cbcurr.
|
||||
*/
|
||||
cbcurr->fn(cbcurr->ctx);
|
||||
sfree(cbcurr);
|
||||
cbcurr = NULL;
|
||||
|
||||
done_something = true;
|
||||
}
|
||||
return done_something;
|
||||
}
|
||||
|
||||
bool toplevel_callback_pending(void)
|
||||
{
|
||||
return cbcurr != NULL || cbhead != NULL;
|
||||
}
|
||||
@@ -0,0 +1,788 @@
|
||||
/*
|
||||
* cgtest.c: stub file to compile cmdgen.c in self-test mode
|
||||
*/
|
||||
|
||||
/*
|
||||
* Before we #include cmdgen.c, we override some function names for
|
||||
* test purposes. We do this via #define, so that when we link against
|
||||
* modules containing the original versions, we don't get a link-time
|
||||
* symbol clash:
|
||||
*
|
||||
* - Calls to get_random_data() are replaced with the diagnostic
|
||||
* function below, in order to avoid depleting the test system's
|
||||
* /dev/random unnecessarily.
|
||||
*
|
||||
* - Calls to console_get_userpass_input() are replaced with the
|
||||
* diagnostic function below, so that I can run tests in an
|
||||
* automated manner and provide their interactive passphrase
|
||||
* inputs.
|
||||
*
|
||||
* - The main() defined by cmdgen.c is renamed to cmdgen_main(); in
|
||||
* this file I define another main() which calls the former
|
||||
* repeatedly to run tests.
|
||||
*/
|
||||
#define get_random_data get_random_data_diagnostic
|
||||
#define console_get_userpass_input console_get_userpass_input_diagnostic
|
||||
#define main cmdgen_main
|
||||
#define ppk_save_default_parameters ppk_save_cgtest_parameters
|
||||
|
||||
#include "cmdgen.c"
|
||||
|
||||
#undef get_random_data
|
||||
#undef console_get_userpass_input
|
||||
#undef main
|
||||
|
||||
static bool cgtest_verbose = false;
|
||||
|
||||
const struct ppk_save_parameters ppk_save_cgtest_parameters = {
|
||||
/* Replacement set of key derivation parameters that make this
|
||||
* test suite run a bit faster and also add determinism: we don't
|
||||
* try to auto-scale the number of passes (in case it gets
|
||||
* different answers twice in the test suite when we were
|
||||
* expecting two key files to compare equal), and we specify a
|
||||
* passphrase salt. */
|
||||
.fmt_version = 3,
|
||||
.argon2_flavour = Argon2id,
|
||||
.argon2_mem = 16,
|
||||
.argon2_passes_auto = false,
|
||||
.argon2_passes = 2,
|
||||
.argon2_parallelism = 1,
|
||||
.salt = (const uint8_t *)"SameSaltEachTime",
|
||||
.saltlen = 16,
|
||||
};
|
||||
|
||||
/*
|
||||
* Define the special versions of get_random_data and
|
||||
* console_get_userpass_input that we need for this test rig.
|
||||
*/
|
||||
|
||||
char *get_random_data_diagnostic(int len, const char *device)
|
||||
{
|
||||
char *buf = snewn(len, char);
|
||||
memset(buf, 'x', len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int nprompts, promptsgot;
|
||||
static const char *prompts[3];
|
||||
int console_get_userpass_input_diagnostic(prompts_t *p)
|
||||
{
|
||||
size_t i;
|
||||
int ret = 1;
|
||||
for (i = 0; i < p->n_prompts; i++) {
|
||||
if (promptsgot < nprompts) {
|
||||
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->s);
|
||||
} else {
|
||||
promptsgot++; /* track number of requests anyway */
|
||||
ret = 0;
|
||||
if (cgtest_verbose)
|
||||
printf(" prompt \"%s\": no response preloaded\n",
|
||||
p->prompts[i]->prompt);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
static int passes, fails;
|
||||
|
||||
void setup_passphrases(char *first, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *next;
|
||||
|
||||
nprompts = 0;
|
||||
if (first) {
|
||||
prompts[nprompts++] = first;
|
||||
va_start(ap, first);
|
||||
while ((next = va_arg(ap, char *)) != NULL) {
|
||||
assert(nprompts < lenof(prompts));
|
||||
prompts[nprompts++] = next;
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
void test(int retval, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, argc, ret;
|
||||
char **argv;
|
||||
|
||||
argc = 0;
|
||||
va_start(ap, retval);
|
||||
while (va_arg(ap, char *) != NULL)
|
||||
argc++;
|
||||
va_end(ap);
|
||||
|
||||
argv = snewn(argc+1, char *);
|
||||
va_start(ap, retval);
|
||||
for (i = 0; i <= argc; i++)
|
||||
argv[i] = va_arg(ap, char *);
|
||||
va_end(ap);
|
||||
|
||||
promptsgot = 0;
|
||||
if (cgtest_verbose) {
|
||||
printf("run:");
|
||||
for (int i = 0; i < argc; i++) {
|
||||
static const char okchars[] =
|
||||
"0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ%+,-./:=[]^_";
|
||||
const char *arg = argv[i];
|
||||
|
||||
printf(" ");
|
||||
if (arg[strspn(arg, okchars)]) {
|
||||
printf("'");
|
||||
for (const char *c = argv[i]; *c; c++) {
|
||||
if (*c == '\'') {
|
||||
printf("'\\''");
|
||||
} else {
|
||||
putchar(*c);
|
||||
}
|
||||
}
|
||||
printf("'");
|
||||
} else {
|
||||
fputs(arg, stdout);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
ret = cmdgen_main(argc, argv);
|
||||
random_clear();
|
||||
|
||||
if (ret != retval) {
|
||||
printf("FAILED retval (exp %d got %d):", retval, ret);
|
||||
for (i = 0; i < argc; i++)
|
||||
printf(" %s", argv[i]);
|
||||
printf("\n");
|
||||
fails++;
|
||||
} else if (promptsgot != nprompts) {
|
||||
printf("FAILED nprompts (exp %d got %d):", nprompts, promptsgot);
|
||||
for (i = 0; i < argc; i++)
|
||||
printf(" %s", argv[i]);
|
||||
printf("\n");
|
||||
fails++;
|
||||
} else {
|
||||
passes++;
|
||||
}
|
||||
|
||||
sfree(argv);
|
||||
}
|
||||
|
||||
PRINTF_LIKE(3, 4) void filecmp(char *file1, char *file2, char *fmt, ...)
|
||||
{
|
||||
/*
|
||||
* Ideally I should do file comparison myself, to maximise the
|
||||
* portability of this test suite once this application begins
|
||||
* running on non-Unix platforms. For the moment, though,
|
||||
* calling Unix diff is perfectly adequate.
|
||||
*/
|
||||
char *buf;
|
||||
int ret;
|
||||
|
||||
buf = dupprintf("diff -q '%s' '%s'", file1, file2);
|
||||
ret = system(buf);
|
||||
sfree(buf);
|
||||
|
||||
if (ret) {
|
||||
va_list ap;
|
||||
|
||||
printf("FAILED diff (ret=%d): ", ret);
|
||||
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
printf("\n");
|
||||
|
||||
fails++;
|
||||
} else
|
||||
passes++;
|
||||
}
|
||||
|
||||
/*
|
||||
* General-purpose flags word
|
||||
*/
|
||||
#define CGT_FLAGS(X) \
|
||||
X(CGT_TYPE_KNOWN_EARLY) \
|
||||
X(CGT_OPENSSH) \
|
||||
X(CGT_SSHCOM) \
|
||||
X(CGT_SSH_KEYGEN) \
|
||||
X(CGT_ED25519) \
|
||||
/* end of list */
|
||||
|
||||
#define FLAG_SHIFTS(name) name ## _shift,
|
||||
enum { CGT_FLAGS(FLAG_SHIFTS) CGT_dummy_shift };
|
||||
#define FLAG_VALUES(name) name = 1 << name ## _shift,
|
||||
enum { CGT_FLAGS(FLAG_VALUES) CGT_dummy_flag };
|
||||
|
||||
char *cleanup_fp(char *s, unsigned flags)
|
||||
{
|
||||
ptrlen pl = ptrlen_from_asciz(s);
|
||||
static const char separators[] = " \n\t";
|
||||
|
||||
/* Skip initial key type word if we find one */
|
||||
if (ptrlen_startswith(pl, PTRLEN_LITERAL("ssh-"), NULL) ||
|
||||
ptrlen_startswith(pl, PTRLEN_LITERAL("ecdsa-"), NULL))
|
||||
ptrlen_get_word(&pl, separators);
|
||||
|
||||
/* Expect two words giving the key length and the hash */
|
||||
ptrlen bits = ptrlen_get_word(&pl, separators);
|
||||
ptrlen hash = ptrlen_get_word(&pl, separators);
|
||||
|
||||
if (flags & CGT_SSH_KEYGEN) {
|
||||
/* Strip "MD5:" prefix if it's present, and do nothing if it isn't */
|
||||
ptrlen_startswith(hash, PTRLEN_LITERAL("MD5:"), &hash);
|
||||
|
||||
if (flags & CGT_ED25519) {
|
||||
/* OpenSSH ssh-keygen lists ed25519 keys as 256 bits, not 255 */
|
||||
if (ptrlen_eq_string(bits, "256"))
|
||||
bits = PTRLEN_LITERAL("255");
|
||||
}
|
||||
}
|
||||
|
||||
return dupprintf("%.*s %.*s", PTRLEN_PRINTF(bits), PTRLEN_PRINTF(hash));
|
||||
}
|
||||
|
||||
char *get_line(char *filename)
|
||||
{
|
||||
FILE *fp;
|
||||
char *line;
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
return NULL;
|
||||
line = fgetline(fp);
|
||||
fclose(fp);
|
||||
return line;
|
||||
}
|
||||
|
||||
char *get_fp(char *filename, unsigned flags)
|
||||
{
|
||||
char *orig = get_line(filename);
|
||||
if (!orig)
|
||||
return NULL;
|
||||
char *toret = cleanup_fp(orig, flags);
|
||||
sfree(orig);
|
||||
return toret;
|
||||
}
|
||||
|
||||
PRINTF_LIKE(3, 4) void check_fp(char *filename, char *fp, char *fmt, ...)
|
||||
{
|
||||
char *newfp;
|
||||
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
newfp = get_fp(filename, 0);
|
||||
|
||||
if (!strcmp(fp, newfp)) {
|
||||
passes++;
|
||||
} else {
|
||||
va_list ap;
|
||||
|
||||
printf("FAILED check_fp ['%s' != '%s']: ", newfp, fp);
|
||||
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
printf("\n");
|
||||
|
||||
fails++;
|
||||
}
|
||||
|
||||
sfree(newfp);
|
||||
}
|
||||
|
||||
static const struct cgtest_keytype {
|
||||
const char *name;
|
||||
unsigned flags;
|
||||
} cgtest_keytypes[] = {
|
||||
{ "rsa1", CGT_TYPE_KNOWN_EARLY },
|
||||
{ "dsa", CGT_OPENSSH | CGT_SSHCOM },
|
||||
{ "rsa", CGT_OPENSSH | CGT_SSHCOM },
|
||||
{ "ecdsa", CGT_OPENSSH },
|
||||
{ "ed25519", CGT_OPENSSH | CGT_ED25519 },
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
int active[lenof(cgtest_keytypes)], active_value;
|
||||
bool remove_files = true;
|
||||
|
||||
active_value = 0;
|
||||
for (i = 0; i < lenof(cgtest_keytypes); i++)
|
||||
active[i] = active_value;
|
||||
|
||||
while (--argc > 0) {
|
||||
ptrlen arg = ptrlen_from_asciz(*++argv);
|
||||
if (ptrlen_eq_string(arg, "-v") ||
|
||||
ptrlen_eq_string(arg, "--verbose")) {
|
||||
cgtest_verbose = true;
|
||||
} else if (ptrlen_eq_string(arg, "--keep")) {
|
||||
remove_files = false;
|
||||
} else if (ptrlen_eq_string(arg, "--help")) {
|
||||
printf("usage: cgtest [options] [key types]\n");
|
||||
printf("options: -v, --verbose "
|
||||
"print more output during tests\n");
|
||||
printf(" --keep "
|
||||
"do not delete the temporary output files\n");
|
||||
printf(" --help "
|
||||
"display this help text\n");
|
||||
printf("key types: ");
|
||||
for (i = 0; i < lenof(cgtest_keytypes); i++)
|
||||
printf("%s%s", i ? ", " : "", cgtest_keytypes[i].name);
|
||||
printf("\n");
|
||||
return 0;
|
||||
} else if (!ptrlen_startswith(arg, PTRLEN_LITERAL("-"), NULL)) {
|
||||
for (i = 0; i < lenof(cgtest_keytypes); i++)
|
||||
if (ptrlen_eq_string(arg, cgtest_keytypes[i].name))
|
||||
break;
|
||||
if (i == lenof(cgtest_keytypes)) {
|
||||
fprintf(stderr, "cgtest: unrecognised key type '%.*s'\n",
|
||||
PTRLEN_PRINTF(arg));
|
||||
return 1;
|
||||
}
|
||||
active_value = 1; /* disables all keys not explicitly enabled */
|
||||
active[i] = active_value;
|
||||
} else {
|
||||
fprintf(stderr, "cgtest: unrecognised option '%.*s'\n",
|
||||
PTRLEN_PRINTF(arg));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
passes = fails = 0;
|
||||
|
||||
for (i = 0; i < lenof(cgtest_keytypes); i++) {
|
||||
if (active[i] != active_value)
|
||||
continue;
|
||||
|
||||
const struct cgtest_keytype *keytype = &cgtest_keytypes[i];
|
||||
bool supports_openssh = keytype->flags & CGT_OPENSSH;
|
||||
bool supports_sshcom = keytype->flags & CGT_SSHCOM;
|
||||
bool type_known_early = keytype->flags & CGT_TYPE_KNOWN_EARLY;
|
||||
|
||||
char filename[128], osfilename[128], scfilename[128];
|
||||
char pubfilename[128], tmpfilename1[128], tmpfilename2[128];
|
||||
char *fps[SSH_N_FPTYPES];
|
||||
|
||||
sprintf(filename, "test-%s.ppk", keytype->name);
|
||||
sprintf(pubfilename, "test-%s.pub", keytype->name);
|
||||
sprintf(osfilename, "test-%s.os", keytype->name);
|
||||
sprintf(scfilename, "test-%s.sc", keytype->name);
|
||||
sprintf(tmpfilename1, "test-%s.tmp1", keytype->name);
|
||||
sprintf(tmpfilename2, "test-%s.tmp2", keytype->name);
|
||||
|
||||
/*
|
||||
* Create an encrypted key.
|
||||
*/
|
||||
setup_passphrases("sponge", "sponge", NULL);
|
||||
test(0, "puttygen", "-t", keytype->name, "-o", filename, NULL);
|
||||
|
||||
/*
|
||||
* List the public key in OpenSSH format.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", "-L", filename, "-o", pubfilename, NULL);
|
||||
for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++) {
|
||||
const char *fpname = (fptype == SSH_FPTYPE_MD5 ? "md5" : "sha256");
|
||||
char *cmdbuf;
|
||||
char *fp = NULL;
|
||||
cmdbuf = dupprintf("ssh-keygen -E %s -l -f '%s' > '%s'",
|
||||
fpname, pubfilename, tmpfilename1);
|
||||
if (cgtest_verbose)
|
||||
printf("OpenSSH %s fp check: %s\n", fpname, cmdbuf);
|
||||
if (system(cmdbuf) ||
|
||||
(fp = get_fp(tmpfilename1,
|
||||
CGT_SSH_KEYGEN | keytype->flags)) == NULL) {
|
||||
printf("UNABLE to test fingerprint matching against "
|
||||
"OpenSSH\n");
|
||||
}
|
||||
sfree(cmdbuf);
|
||||
if (fp && cgtest_verbose) {
|
||||
char *line = get_line(tmpfilename1);
|
||||
printf("OpenSSH %s fp: %s\n", fpname, line);
|
||||
printf("Cleaned up: %s\n", fp);
|
||||
sfree(line);
|
||||
}
|
||||
fps[fptype] = fp;
|
||||
}
|
||||
|
||||
/*
|
||||
* List the public key in IETF/ssh.com format.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", "-p", filename, NULL);
|
||||
|
||||
/*
|
||||
* List the fingerprint of the key.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++) {
|
||||
const char *fpname = (fptype == SSH_FPTYPE_MD5 ? "md5" : "sha256");
|
||||
test(0, "puttygen", "-E", fpname, "-l", filename,
|
||||
"-o", tmpfilename1, NULL);
|
||||
if (!fps[fptype]) {
|
||||
/*
|
||||
* If we can't test fingerprints against OpenSSH, we
|
||||
* can at the very least test equality of all the
|
||||
* fingerprints we generate of this key throughout
|
||||
* testing.
|
||||
*/
|
||||
fps[fptype] = get_fp(tmpfilename1, 0);
|
||||
} else {
|
||||
check_fp(tmpfilename1, fps[fptype], "%s initial %s fp",
|
||||
keytype->name, fpname);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the comment of the key; this _does_ require a
|
||||
* passphrase owing to the tamperproofing.
|
||||
*
|
||||
* NOTE: In SSH-1, this only requires a passphrase because
|
||||
* of inadequacies of the loading and saving mechanisms. In
|
||||
* _principle_, it should be perfectly possible to modify
|
||||
* the comment on an SSH-1 key without requiring a
|
||||
* passphrase; the only reason I can't do it is because my
|
||||
* loading and saving mechanisms don't include a method of
|
||||
* loading all the key data without also trying to decrypt
|
||||
* the private section.
|
||||
*
|
||||
* I don't consider this to be a problem worth solving,
|
||||
* because (a) to fix it would probably end up bloating
|
||||
* PuTTY proper, and (b) SSH-1 is on the way out anyway so
|
||||
* it shouldn't be highly significant. If it seriously
|
||||
* bothers anyone then perhaps I _might_ be persuadable.
|
||||
*/
|
||||
setup_passphrases("sponge", NULL);
|
||||
test(0, "puttygen", "-C", "new-comment", filename, NULL);
|
||||
|
||||
/*
|
||||
* Change the passphrase to nothing.
|
||||
*/
|
||||
setup_passphrases("sponge", "", "", NULL);
|
||||
test(0, "puttygen", "-P", filename, NULL);
|
||||
|
||||
/*
|
||||
* Change the comment of the key again; this time we expect no
|
||||
* passphrase to be required.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", "-C", "new-comment-2", filename, NULL);
|
||||
|
||||
/*
|
||||
* Export the private key into OpenSSH format; no passphrase
|
||||
* should be required since the key is currently unencrypted.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(supports_openssh ? 0 : 1,
|
||||
"puttygen", "-O", "private-openssh", "-o", osfilename,
|
||||
filename, NULL);
|
||||
|
||||
if (supports_openssh) {
|
||||
/*
|
||||
* List the fingerprint of the OpenSSH-formatted key.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);
|
||||
check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT],
|
||||
"%s openssh clear fp", keytype->name);
|
||||
|
||||
/*
|
||||
* List the public half of the OpenSSH-formatted key in
|
||||
* OpenSSH format.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", "-L", osfilename, NULL);
|
||||
|
||||
/*
|
||||
* List the public half of the OpenSSH-formatted key in
|
||||
* IETF/ssh.com format.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", "-p", osfilename, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Export the private key into ssh.com format; no passphrase
|
||||
* should be required since the key is currently unencrypted.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(supports_sshcom ? 0 : 1,
|
||||
"puttygen", "-O", "private-sshcom",
|
||||
"-o", scfilename, filename, NULL);
|
||||
|
||||
if (supports_sshcom) {
|
||||
/*
|
||||
* List the fingerprint of the ssh.com-formatted key.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);
|
||||
check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT],
|
||||
"%s ssh.com clear fp", keytype->name);
|
||||
|
||||
/*
|
||||
* List the public half of the ssh.com-formatted key in
|
||||
* OpenSSH format.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", "-L", scfilename, NULL);
|
||||
|
||||
/*
|
||||
* List the public half of the ssh.com-formatted key in
|
||||
* IETF/ssh.com format.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", "-p", scfilename, NULL);
|
||||
}
|
||||
|
||||
if (supports_openssh && supports_sshcom) {
|
||||
/*
|
||||
* Convert from OpenSSH into ssh.com.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", osfilename, "-o", tmpfilename1,
|
||||
"-O", "private-sshcom", NULL);
|
||||
|
||||
/*
|
||||
* Convert from ssh.com back into a PuTTY key,
|
||||
* supplying the same comment as we had before we
|
||||
* started to ensure the comparison works.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
|
||||
"-o", tmpfilename2, NULL);
|
||||
|
||||
/*
|
||||
* See if the PuTTY key thus generated is the same as
|
||||
* the original.
|
||||
*/
|
||||
filecmp(filename, tmpfilename2,
|
||||
"p->o->s->p clear %s", keytype->name);
|
||||
|
||||
/*
|
||||
* Convert from ssh.com to OpenSSH.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", scfilename, "-o", tmpfilename1,
|
||||
"-O", "private-openssh", NULL);
|
||||
|
||||
/*
|
||||
* Convert from OpenSSH back into a PuTTY key,
|
||||
* supplying the same comment as we had before we
|
||||
* started to ensure the comparison works.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
|
||||
"-o", tmpfilename2, NULL);
|
||||
|
||||
/*
|
||||
* See if the PuTTY key thus generated is the same as
|
||||
* the original.
|
||||
*/
|
||||
filecmp(filename, tmpfilename2,
|
||||
"p->s->o->p clear %s", keytype->name);
|
||||
|
||||
/*
|
||||
* Finally, do a round-trip conversion between PuTTY
|
||||
* and ssh.com without involving OpenSSH, to test that
|
||||
* the key comment is preserved in that case.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1,
|
||||
filename, NULL);
|
||||
setup_passphrases(NULL);
|
||||
test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);
|
||||
filecmp(filename, tmpfilename2,
|
||||
"p->s->p clear %s", keytype->name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that mismatched passphrases cause an error.
|
||||
*/
|
||||
setup_passphrases("sponge2", "sponge3", NULL);
|
||||
test(1, "puttygen", "-P", filename, NULL);
|
||||
|
||||
/*
|
||||
* Put a passphrase back on.
|
||||
*/
|
||||
setup_passphrases("sponge2", "sponge2", NULL);
|
||||
test(0, "puttygen", "-P", filename, NULL);
|
||||
|
||||
/*
|
||||
* Export the private key into OpenSSH format, this time
|
||||
* while encrypted.
|
||||
*/
|
||||
if (!supports_openssh && type_known_early) {
|
||||
/* We'll know far enough in advance that this combination
|
||||
* is going to fail that we never ask for the passphrase */
|
||||
setup_passphrases(NULL);
|
||||
} else {
|
||||
setup_passphrases("sponge2", NULL);
|
||||
}
|
||||
|
||||
test(supports_openssh ? 0 : 1,
|
||||
"puttygen", "-O", "private-openssh", "-o", osfilename,
|
||||
filename, NULL);
|
||||
|
||||
if (supports_openssh) {
|
||||
/*
|
||||
* List the fingerprint of the OpenSSH-formatted key.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);
|
||||
check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT],
|
||||
"%s openssh encrypted fp", keytype->name);
|
||||
|
||||
/*
|
||||
* List the public half of the OpenSSH-formatted key in
|
||||
* OpenSSH format.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", "-L", osfilename, NULL);
|
||||
|
||||
/*
|
||||
* List the public half of the OpenSSH-formatted key in
|
||||
* IETF/ssh.com format.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", "-p", osfilename, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Export the private key into ssh.com format, this time
|
||||
* while encrypted. For RSA1 keys, this should give an
|
||||
* error.
|
||||
*/
|
||||
if (!supports_sshcom && type_known_early) {
|
||||
/* We'll know far enough in advance that this combination
|
||||
* is going to fail that we never ask for the passphrase */
|
||||
setup_passphrases(NULL);
|
||||
} else {
|
||||
setup_passphrases("sponge2", NULL);
|
||||
}
|
||||
|
||||
test(supports_sshcom ? 0 : 1,
|
||||
"puttygen", "-O", "private-sshcom", "-o", scfilename,
|
||||
filename, NULL);
|
||||
|
||||
if (supports_sshcom) {
|
||||
/*
|
||||
* List the fingerprint of the ssh.com-formatted key.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);
|
||||
check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT],
|
||||
"%s ssh.com encrypted fp", keytype->name);
|
||||
|
||||
/*
|
||||
* List the public half of the ssh.com-formatted key in
|
||||
* OpenSSH format.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", "-L", scfilename, NULL);
|
||||
|
||||
/*
|
||||
* List the public half of the ssh.com-formatted key in
|
||||
* IETF/ssh.com format.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", "-p", scfilename, NULL);
|
||||
}
|
||||
|
||||
if (supports_openssh && supports_sshcom) {
|
||||
/*
|
||||
* Convert from OpenSSH into ssh.com.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", osfilename, "-o", tmpfilename1,
|
||||
"-O", "private-sshcom", NULL);
|
||||
|
||||
/*
|
||||
* Convert from ssh.com back into a PuTTY key,
|
||||
* supplying the same comment as we had before we
|
||||
* started to ensure the comparison works.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
|
||||
"-o", tmpfilename2, NULL);
|
||||
|
||||
/*
|
||||
* See if the PuTTY key thus generated is the same as
|
||||
* the original.
|
||||
*/
|
||||
filecmp(filename, tmpfilename2,
|
||||
"p->o->s->p encrypted %s", keytype->name);
|
||||
|
||||
/*
|
||||
* Convert from ssh.com to OpenSSH.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", scfilename, "-o", tmpfilename1,
|
||||
"-O", "private-openssh", NULL);
|
||||
|
||||
/*
|
||||
* Convert from OpenSSH back into a PuTTY key,
|
||||
* supplying the same comment as we had before we
|
||||
* started to ensure the comparison works.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
|
||||
"-o", tmpfilename2, NULL);
|
||||
|
||||
/*
|
||||
* See if the PuTTY key thus generated is the same as
|
||||
* the original.
|
||||
*/
|
||||
filecmp(filename, tmpfilename2,
|
||||
"p->s->o->p encrypted %s", keytype->name);
|
||||
|
||||
/*
|
||||
* Finally, do a round-trip conversion between PuTTY
|
||||
* and ssh.com without involving OpenSSH, to test that
|
||||
* the key comment is preserved in that case.
|
||||
*/
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1,
|
||||
filename, NULL);
|
||||
setup_passphrases("sponge2", NULL);
|
||||
test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);
|
||||
filecmp(filename, tmpfilename2,
|
||||
"p->s->p encrypted %s", keytype->name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load with the wrong passphrase.
|
||||
*/
|
||||
setup_passphrases("sponge8", NULL);
|
||||
test(1, "puttygen", "-C", "spurious-new-comment", filename, NULL);
|
||||
|
||||
/*
|
||||
* Load a totally bogus file.
|
||||
*/
|
||||
setup_passphrases(NULL);
|
||||
test(1, "puttygen", "-C", "spurious-new-comment", pubfilename, NULL);
|
||||
|
||||
for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++)
|
||||
sfree(fps[fptype]);
|
||||
|
||||
if (remove_files) {
|
||||
remove(filename);
|
||||
remove(pubfilename);
|
||||
remove(osfilename);
|
||||
remove(scfilename);
|
||||
remove(tmpfilename1);
|
||||
remove(tmpfilename2);
|
||||
}
|
||||
}
|
||||
printf("%d passes, %d fails\n", passes, fails);
|
||||
return fails == 0 ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
This subdirectory contains a general character-set conversion
|
||||
library, used in the Unix port of PuTTY, and available for use in
|
||||
other ports if it should happen to be useful.
|
||||
|
||||
This is a variant of a library that's currently used in some other
|
||||
programs such as Timber and Halibut. At some future date, we would
|
||||
like to merge the two libraries, so that all programs use the same
|
||||
libcharset.
|
||||
|
||||
It is therefore a _strong_ design goal that this library should remain
|
||||
perfectly general, and not tied to particulars of PuTTY. It must not
|
||||
reference any code outside its own subdirectory; it should not have
|
||||
PuTTY-specific helper routines added to it unless they can be
|
||||
documented in a general manner which might make them useful in other
|
||||
circumstances as well.
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* charset.h - header file for general character set conversion
|
||||
* routines.
|
||||
*/
|
||||
|
||||
#ifndef charset_charset_h
|
||||
#define charset_charset_h
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* Enumeration that lists all the multibyte or single-byte
|
||||
* character sets known to this library.
|
||||
*/
|
||||
typedef enum {
|
||||
CS_NONE, /* used for reporting errors, etc */
|
||||
CS_ISO8859_1,
|
||||
CS_ISO8859_1_X11, /* X font encoding with VT100 glyphs */
|
||||
CS_ISO8859_2,
|
||||
CS_ISO8859_3,
|
||||
CS_ISO8859_4,
|
||||
CS_ISO8859_5,
|
||||
CS_ISO8859_6,
|
||||
CS_ISO8859_7,
|
||||
CS_ISO8859_8,
|
||||
CS_ISO8859_9,
|
||||
CS_ISO8859_10,
|
||||
CS_ISO8859_11,
|
||||
CS_ISO8859_13,
|
||||
CS_ISO8859_14,
|
||||
CS_ISO8859_15,
|
||||
CS_ISO8859_16,
|
||||
CS_CP437,
|
||||
CS_CP850,
|
||||
CS_CP852,
|
||||
CS_CP866,
|
||||
CS_CP1250,
|
||||
CS_CP1251,
|
||||
CS_CP1252,
|
||||
CS_CP1253,
|
||||
CS_CP1254,
|
||||
CS_CP1255,
|
||||
CS_CP1256,
|
||||
CS_CP1257,
|
||||
CS_CP1258,
|
||||
CS_KOI8_R,
|
||||
CS_KOI8_U,
|
||||
CS_MAC_ROMAN,
|
||||
CS_MAC_TURKISH,
|
||||
CS_MAC_CROATIAN,
|
||||
CS_MAC_ICELAND,
|
||||
CS_MAC_ROMANIAN,
|
||||
CS_MAC_GREEK,
|
||||
CS_MAC_CYRILLIC,
|
||||
CS_MAC_THAI,
|
||||
CS_MAC_CENTEURO,
|
||||
CS_MAC_SYMBOL,
|
||||
CS_MAC_DINGBATS,
|
||||
CS_MAC_ROMAN_OLD,
|
||||
CS_MAC_CROATIAN_OLD,
|
||||
CS_MAC_ICELAND_OLD,
|
||||
CS_MAC_ROMANIAN_OLD,
|
||||
CS_MAC_GREEK_OLD,
|
||||
CS_MAC_CYRILLIC_OLD,
|
||||
CS_MAC_UKRAINE,
|
||||
CS_MAC_VT100,
|
||||
CS_MAC_VT100_OLD,
|
||||
CS_VISCII,
|
||||
CS_HP_ROMAN8,
|
||||
CS_DEC_MCS,
|
||||
CS_UTF8
|
||||
} charset_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned long s0;
|
||||
} charset_state;
|
||||
|
||||
/*
|
||||
* Routine to convert a MB/SB character set to Unicode.
|
||||
*
|
||||
* This routine accepts some number of bytes, updates a state
|
||||
* variable, and outputs some number of Unicode characters. There
|
||||
* are no guarantees. You can't even guarantee that at most one
|
||||
* Unicode character will be output per byte you feed in; for
|
||||
* example, suppose you're reading UTF-8, you've seen E1 80, and
|
||||
* then you suddenly see FE. Now you need to output _two_ error
|
||||
* characters - one for the incomplete sequence E1 80, and one for
|
||||
* the completely invalid UTF-8 byte FE.
|
||||
*
|
||||
* Returns the number of wide characters output; will never output
|
||||
* more than the size of the buffer (as specified on input).
|
||||
* Advances the `input' pointer and decrements `inlen', to indicate
|
||||
* how far along the input string it got.
|
||||
*
|
||||
* The sequence of `errlen' wide characters pointed to by `errstr'
|
||||
* will be used to indicate a conversion error. If `errstr' is
|
||||
* NULL, `errlen' will be ignored, and the library will choose
|
||||
* something sensible to do on its own. For Unicode, this will be
|
||||
* U+FFFD (REPLACEMENT CHARACTER).
|
||||
*/
|
||||
|
||||
int charset_to_unicode(const char **input, int *inlen,
|
||||
wchar_t *output, int outlen,
|
||||
int charset, charset_state *state,
|
||||
const wchar_t *errstr, int errlen);
|
||||
|
||||
/*
|
||||
* Routine to convert Unicode to an MB/SB character set.
|
||||
*
|
||||
* This routine accepts some number of Unicode characters, updates
|
||||
* a state variable, and outputs some number of bytes.
|
||||
*
|
||||
* Returns the number of bytes characters output; will never output
|
||||
* more than the size of the buffer (as specified on input), and
|
||||
* will never output a partial MB character. Advances the `input'
|
||||
* pointer and decrements `inlen', to indicate how far along the
|
||||
* input string it got.
|
||||
*
|
||||
* The sequence of `errlen' characters pointed to by `errstr' will
|
||||
* be used to indicate a conversion error. If `errstr' is NULL,
|
||||
* `errlen' will be ignored, and the library will choose something
|
||||
* sensible to do on its own (which will vary depending on the
|
||||
* output charset).
|
||||
*/
|
||||
|
||||
int charset_from_unicode(const wchar_t **input, int *inlen,
|
||||
char *output, int outlen,
|
||||
int charset, charset_state *state,
|
||||
const char *errstr, int errlen);
|
||||
|
||||
/*
|
||||
* Convert X11 encoding names to and from our charset identifiers.
|
||||
*/
|
||||
const char *charset_to_xenc(int charset);
|
||||
int charset_from_xenc(const char *name);
|
||||
|
||||
/*
|
||||
* Convert MIME encoding names to and from our charset identifiers.
|
||||
*/
|
||||
const char *charset_to_mimeenc(int charset);
|
||||
int charset_from_mimeenc(const char *name);
|
||||
|
||||
/*
|
||||
* Convert our own encoding names to and from our charset
|
||||
* identifiers.
|
||||
*/
|
||||
const char *charset_to_localenc(int charset);
|
||||
int charset_from_localenc(const char *name);
|
||||
int charset_localenc_nth(int n);
|
||||
|
||||
/*
|
||||
* Convert Mac OS script/region/font to our charset identifiers.
|
||||
*/
|
||||
int charset_from_macenc(int script, int region, int sysvers,
|
||||
const char *fontname);
|
||||
|
||||
#endif /* charset_charset_h */
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* enum.c - enumerate all charsets defined by the library.
|
||||
*
|
||||
* This file maintains a list of every other source file which
|
||||
* contains ENUM_CHARSET definitions. It #includes each one with
|
||||
* ENUM_CHARSETS defined, which causes those source files to do
|
||||
* nothing at all except call the ENUM_CHARSET macro on each
|
||||
* charset they define.
|
||||
*
|
||||
* This file in turn is included from various other places, with
|
||||
* the ENUM_CHARSET macro defined to various different things. This
|
||||
* allows us to have multiple implementations of the master charset
|
||||
* lookup table (a static one and a dynamic one).
|
||||
*/
|
||||
|
||||
#define ENUM_CHARSETS
|
||||
#include "sbcsdat.c"
|
||||
#include "utf8.c"
|
||||
#undef ENUM_CHARSETS
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* fromucs.c - convert Unicode to other character sets.
|
||||
*/
|
||||
|
||||
#include "charset.h"
|
||||
#include "internal.h"
|
||||
|
||||
struct charset_emit_param {
|
||||
char *output;
|
||||
int outlen;
|
||||
const char *errstr;
|
||||
int errlen;
|
||||
int stopped;
|
||||
};
|
||||
|
||||
static void charset_emit(void *ctx, long int output)
|
||||
{
|
||||
struct charset_emit_param *param = (struct charset_emit_param *)ctx;
|
||||
char outval;
|
||||
char const *p;
|
||||
int outlen;
|
||||
|
||||
if (output == ERROR) {
|
||||
p = param->errstr;
|
||||
outlen = param->errlen;
|
||||
} else {
|
||||
outval = output;
|
||||
p = &outval;
|
||||
outlen = 1;
|
||||
}
|
||||
|
||||
if (param->outlen >= outlen) {
|
||||
while (outlen > 0) {
|
||||
*param->output++ = *p++;
|
||||
param->outlen--;
|
||||
outlen--;
|
||||
}
|
||||
} else {
|
||||
param->stopped = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int charset_from_unicode(const wchar_t **input, int *inlen,
|
||||
char *output, int outlen,
|
||||
int charset, charset_state *state,
|
||||
const char *errstr, int errlen)
|
||||
{
|
||||
charset_spec const *spec = charset_find_spec(charset);
|
||||
charset_state localstate;
|
||||
struct charset_emit_param param;
|
||||
|
||||
param.output = output;
|
||||
param.outlen = outlen;
|
||||
param.stopped = 0;
|
||||
|
||||
/*
|
||||
* charset_emit will expect a valid errstr.
|
||||
*/
|
||||
if (!errstr) {
|
||||
/* *shrug* this is good enough, and consistent across all SBCS... */
|
||||
param.errstr = ".";
|
||||
param.errlen = 1;
|
||||
}
|
||||
param.errstr = errstr;
|
||||
param.errlen = errlen;
|
||||
|
||||
if (!state) {
|
||||
localstate.s0 = 0;
|
||||
} else {
|
||||
localstate = *state; /* structure copy */
|
||||
}
|
||||
state = &localstate;
|
||||
|
||||
while (*inlen > 0) {
|
||||
int lenbefore = param.output - output;
|
||||
spec->write(spec, **input, &localstate, charset_emit, ¶m);
|
||||
if (param.stopped) {
|
||||
/*
|
||||
* The emit function has _tried_ to output some
|
||||
* characters, but ran up against the end of the
|
||||
* buffer. Leave immediately, and return what happened
|
||||
* _before_ attempting to process this character.
|
||||
*/
|
||||
return lenbefore;
|
||||
}
|
||||
if (state)
|
||||
*state = localstate; /* structure copy */
|
||||
(*input)++;
|
||||
(*inlen)--;
|
||||
}
|
||||
return param.output - output;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* internal.h - internal header stuff for the charset library.
|
||||
*/
|
||||
|
||||
#ifndef charset_internal_h
|
||||
#define charset_internal_h
|
||||
|
||||
/* This invariably comes in handy */
|
||||
#define lenof(x) ( sizeof((x)) / sizeof(*(x)) )
|
||||
|
||||
/* This is an invalid Unicode value used to indicate an error. */
|
||||
#define ERROR 0xFFFFL /* Unicode value representing error */
|
||||
|
||||
typedef struct charset_spec charset_spec;
|
||||
typedef struct sbcs_data sbcs_data;
|
||||
|
||||
struct charset_spec {
|
||||
int charset; /* numeric identifier */
|
||||
|
||||
/*
|
||||
* A function to read the character set and output Unicode
|
||||
* characters. The `emit' function expects to get Unicode chars
|
||||
* passed to it; it should be sent ERROR for any encoding error
|
||||
* on the input.
|
||||
*/
|
||||
void (*read)(charset_spec const *charset, long int input_chr,
|
||||
charset_state *state,
|
||||
void (*emit)(void *ctx, long int output), void *emitctx);
|
||||
/*
|
||||
* A function to read Unicode characters and output in this
|
||||
* character set. The `emit' function expects to get byte
|
||||
* values passed to it; it should be sent ERROR for any
|
||||
* non-representable characters on the input.
|
||||
*/
|
||||
void (*write)(charset_spec const *charset, long int input_chr,
|
||||
charset_state *state,
|
||||
void (*emit)(void *ctx, long int output), void *emitctx);
|
||||
void const *data;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the format of `data' used by the SBCS read and write
|
||||
* functions; so it's the format used in all SBCS definitions.
|
||||
*/
|
||||
struct sbcs_data {
|
||||
/*
|
||||
* This is a simple mapping table converting each SBCS position
|
||||
* to a Unicode code point. Some positions may contain ERROR,
|
||||
* indicating that that byte value is not defined in the SBCS
|
||||
* in question and its occurrence in input is an error.
|
||||
*/
|
||||
unsigned long sbcs2ucs[256];
|
||||
|
||||
/*
|
||||
* This lookup table is used to convert Unicode back to the
|
||||
* SBCS. It consists of the valid byte values in the SBCS,
|
||||
* sorted in order of their Unicode translation. So given a
|
||||
* Unicode value U, you can do a binary search on this table
|
||||
* using the above table as a lookup: when testing the Xth
|
||||
* position in this table, you branch according to whether
|
||||
* sbcs2ucs[ucs2sbcs[X]] is less than, greater than, or equal
|
||||
* to U.
|
||||
*
|
||||
* Note that since there may be fewer than 256 valid byte
|
||||
* values in a particular SBCS, we must supply the length of
|
||||
* this table as well as the contents.
|
||||
*/
|
||||
unsigned char ucs2sbcs[256];
|
||||
int nvalid;
|
||||
};
|
||||
|
||||
/*
|
||||
* Prototypes for internal library functions.
|
||||
*/
|
||||
charset_spec const *charset_find_spec(int charset);
|
||||
void read_sbcs(charset_spec const *charset, long int input_chr,
|
||||
charset_state *state,
|
||||
void (*emit)(void *ctx, long int output), void *emitctx);
|
||||
void write_sbcs(charset_spec const *charset, long int input_chr,
|
||||
charset_state *state,
|
||||
void (*emit)(void *ctx, long int output), void *emitctx);
|
||||
|
||||
/*
|
||||
* Placate compiler warning about unused parameters, of which we
|
||||
* expect to have some in this library.
|
||||
*/
|
||||
#define UNUSEDARG(x) ( (x) = (x) )
|
||||
|
||||
#endif /* charset_internal_h */
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* local.c - translate our internal character set codes to and from
|
||||
* our own set of plausibly legible character-set names. Also
|
||||
* provides a canonical name for each encoding (useful for software
|
||||
* announcing what character set it will be using), and a set of
|
||||
* enumeration functions which return a list of supported
|
||||
* encodings one by one.
|
||||
*
|
||||
* charset_from_localenc will attempt all other text translations
|
||||
* as well as this table, to maximise the number of different ways
|
||||
* you can select a supported charset.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include "charset.h"
|
||||
#include "internal.h"
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
int charset;
|
||||
int return_in_enum; /* enumeration misses some charsets */
|
||||
} localencs[] = {
|
||||
{ "<UNKNOWN>", CS_NONE, 0 },
|
||||
{ "UTF-8", CS_UTF8, 1 },
|
||||
{ "ISO-8859-1", CS_ISO8859_1, 1 },
|
||||
{ "ISO-8859-1 with X11 line drawing", CS_ISO8859_1_X11, 0 },
|
||||
{ "ISO-8859-2", CS_ISO8859_2, 1 },
|
||||
{ "ISO-8859-3", CS_ISO8859_3, 1 },
|
||||
{ "ISO-8859-4", CS_ISO8859_4, 1 },
|
||||
{ "ISO-8859-5", CS_ISO8859_5, 1 },
|
||||
{ "ISO-8859-6", CS_ISO8859_6, 1 },
|
||||
{ "ISO-8859-7", CS_ISO8859_7, 1 },
|
||||
{ "ISO-8859-8", CS_ISO8859_8, 1 },
|
||||
{ "ISO-8859-9", CS_ISO8859_9, 1 },
|
||||
{ "ISO-8859-10", CS_ISO8859_10, 1 },
|
||||
{ "ISO-8859-11", CS_ISO8859_11, 1 },
|
||||
{ "ISO-8859-13", CS_ISO8859_13, 1 },
|
||||
{ "ISO-8859-14", CS_ISO8859_14, 1 },
|
||||
{ "ISO-8859-15", CS_ISO8859_15, 1 },
|
||||
{ "ISO-8859-16", CS_ISO8859_16, 1 },
|
||||
{ "CP437", CS_CP437, 1 },
|
||||
{ "CP850", CS_CP850, 1 },
|
||||
{ "CP852", CS_CP852, 1 },
|
||||
{ "CP866", CS_CP866, 1 },
|
||||
{ "CP1250", CS_CP1250, 1 },
|
||||
{ "CP1251", CS_CP1251, 1 },
|
||||
{ "CP1252", CS_CP1252, 1 },
|
||||
{ "CP1253", CS_CP1253, 1 },
|
||||
{ "CP1254", CS_CP1254, 1 },
|
||||
{ "CP1255", CS_CP1255, 1 },
|
||||
{ "CP1256", CS_CP1256, 1 },
|
||||
{ "CP1257", CS_CP1257, 1 },
|
||||
{ "CP1258", CS_CP1258, 1 },
|
||||
{ "KOI8-R", CS_KOI8_R, 1 },
|
||||
{ "KOI8-U", CS_KOI8_U, 1 },
|
||||
{ "Mac Roman", CS_MAC_ROMAN, 1 },
|
||||
{ "Mac Turkish", CS_MAC_TURKISH, 1 },
|
||||
{ "Mac Croatian", CS_MAC_CROATIAN, 1 },
|
||||
{ "Mac Iceland", CS_MAC_ICELAND, 1 },
|
||||
{ "Mac Romanian", CS_MAC_ROMANIAN, 1 },
|
||||
{ "Mac Greek", CS_MAC_GREEK, 1 },
|
||||
{ "Mac Cyrillic", CS_MAC_CYRILLIC, 1 },
|
||||
{ "Mac Thai", CS_MAC_THAI, 1 },
|
||||
{ "Mac Centeuro", CS_MAC_CENTEURO, 1 },
|
||||
{ "Mac Symbol", CS_MAC_SYMBOL, 1 },
|
||||
{ "Mac Dingbats", CS_MAC_DINGBATS, 1 },
|
||||
{ "Mac Roman (old)", CS_MAC_ROMAN_OLD, 0 },
|
||||
{ "Mac Croatian (old)", CS_MAC_CROATIAN_OLD, 0 },
|
||||
{ "Mac Iceland (old)", CS_MAC_ICELAND_OLD, 0 },
|
||||
{ "Mac Romanian (old)", CS_MAC_ROMANIAN_OLD, 0 },
|
||||
{ "Mac Greek (old)", CS_MAC_GREEK_OLD, 0 },
|
||||
{ "Mac Cyrillic (old)", CS_MAC_CYRILLIC_OLD, 0 },
|
||||
{ "Mac Ukraine", CS_MAC_UKRAINE, 1 },
|
||||
{ "Mac VT100", CS_MAC_VT100, 1 },
|
||||
{ "Mac VT100 (old)", CS_MAC_VT100_OLD, 0 },
|
||||
{ "VISCII", CS_VISCII, 1 },
|
||||
{ "HP ROMAN8", CS_HP_ROMAN8, 1 },
|
||||
{ "DEC MCS", CS_DEC_MCS, 1 },
|
||||
};
|
||||
|
||||
const char *charset_to_localenc(int charset)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)lenof(localencs); i++)
|
||||
if (charset == localencs[i].charset)
|
||||
return localencs[i].name;
|
||||
|
||||
return NULL; /* not found */
|
||||
}
|
||||
|
||||
int charset_from_localenc(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( (i = charset_from_mimeenc(name)) != CS_NONE)
|
||||
return i;
|
||||
if ( (i = charset_from_xenc(name)) != CS_NONE)
|
||||
return i;
|
||||
|
||||
for (i = 0; i < (int)lenof(localencs); i++) {
|
||||
const char *p, *q;
|
||||
p = name;
|
||||
q = localencs[i].name;
|
||||
while (*p || *q) {
|
||||
if (tolower((unsigned char)*p) != tolower((unsigned char)*q))
|
||||
break;
|
||||
p++; q++;
|
||||
}
|
||||
if (!*p && !*q)
|
||||
return localencs[i].charset;
|
||||
}
|
||||
|
||||
return CS_NONE; /* not found */
|
||||
}
|
||||
|
||||
int charset_localenc_nth(int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)lenof(localencs); i++)
|
||||
if (localencs[i].return_in_enum && !n--)
|
||||
return localencs[i].charset;
|
||||
|
||||
return CS_NONE; /* end of list */
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2003 Ben Harris
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
/*
|
||||
* macenc.c -- Convert a Mac OS script/region/font combination to our
|
||||
* internal charset code.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "charset.h"
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* These are defined by Mac OS's <Script.h>, but we'd like to be
|
||||
* independent of that.
|
||||
*/
|
||||
|
||||
#define smRoman 0
|
||||
#define smJapanese 1
|
||||
#define smTradChinese 2
|
||||
#define smKorean 3
|
||||
#define smArabic 4
|
||||
#define smHebrew 5
|
||||
#define smCyrillic 7
|
||||
#define smDevenagari 9
|
||||
#define smGurmukhi 10
|
||||
#define smGujurati 11
|
||||
#define smThai 21
|
||||
#define smSimpChinese 25
|
||||
#define smTibetan 26
|
||||
#define smEthiopic 28
|
||||
#define smCentralEuroRoman 29
|
||||
|
||||
#define verGreece 20
|
||||
#define verIceland 21
|
||||
#define verTurkey 24
|
||||
#define verYugoCroatian 25
|
||||
#define verRomania 39
|
||||
#define verFaroeIsl 47
|
||||
#define verIran 48
|
||||
#define verRussia 49
|
||||
#define verSlovenian 66
|
||||
#define verCroatia 68
|
||||
#define verBulgaria 72
|
||||
#define verScottishGaelic 75
|
||||
#define verManxGaelic 76
|
||||
#define verBreton 77
|
||||
#define verNunavut 78
|
||||
#define verWelsh 79
|
||||
#define verIrishGaelicScript 81
|
||||
|
||||
static const struct {
|
||||
int script;
|
||||
int region;
|
||||
int sysvermin;
|
||||
char const *fontname;
|
||||
int charset;
|
||||
} macencs[] = {
|
||||
{ smRoman, -1, 0x850, "VT100", CS_MAC_VT100 },
|
||||
{ smRoman, -1, 0, "VT100", CS_MAC_VT100_OLD },
|
||||
/*
|
||||
* From here on, this table is largely derived from
|
||||
* <http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/README.TXT>,
|
||||
* with _OLD version added based on the comments in individual
|
||||
* mapping files.
|
||||
*/
|
||||
{ smRoman, -1, 0, "Symbol", CS_MAC_SYMBOL },
|
||||
{ smRoman, -1, 0, "Zapf Dingbats", CS_MAC_DINGBATS },
|
||||
{ smRoman, verTurkey, 0, NULL, CS_MAC_TURKISH },
|
||||
{ smRoman, verYugoCroatian, 0x850, NULL, CS_MAC_CROATIAN },
|
||||
{ smRoman, verYugoCroatian, 0, NULL, CS_MAC_CROATIAN_OLD },
|
||||
{ smRoman, verSlovenian, 0x850, NULL, CS_MAC_CROATIAN },
|
||||
{ smRoman, verSlovenian, 0, NULL, CS_MAC_CROATIAN_OLD },
|
||||
{ smRoman, verCroatia, 0x850, NULL, CS_MAC_CROATIAN },
|
||||
{ smRoman, verCroatia, 0, NULL, CS_MAC_CROATIAN_OLD },
|
||||
{ smRoman, verIceland, 0x850, NULL, CS_MAC_ICELAND },
|
||||
{ smRoman, verIceland, 0, NULL, CS_MAC_ICELAND_OLD },
|
||||
{ smRoman, verFaroeIsl, 0x850, NULL, CS_MAC_ICELAND },
|
||||
{ smRoman, verFaroeIsl, 0, NULL, CS_MAC_ICELAND_OLD },
|
||||
{ smRoman, verRomania, 0x850, NULL, CS_MAC_ROMANIAN },
|
||||
{ smRoman, verRomania, 0, NULL, CS_MAC_ROMANIAN_OLD },
|
||||
#if 0 /* No mapping table on ftp.unicode.org */
|
||||
{ smRoman, verIreland, 0x850, NULL, CS_MAC_CELTIC },
|
||||
{ smRoman, verIreland, 0, NULL, CS_MAC_CELTIC_OLD },
|
||||
{ smRoman, verScottishGaelic, 0x850, NULL, CS_MAC_CELTIC },
|
||||
{ smRoman, verScottishGaelic, 0, NULL, CS_MAC_CELTIC_OLD },
|
||||
{ smRoman, verManxGaelic, 0x850, NULL, CS_MAC_CELTIC },
|
||||
{ smRoman, verManxGaelic, 0, NULL, CS_MAC_CELTIC_OLD },
|
||||
{ smRoman, verBreton, 0x850, NULL, CS_MAC_CELTIC },
|
||||
{ smRoman, verBreton, 0, NULL, CS_MAC_CELTIC_OLD },
|
||||
{ smRoman, verWelsh, 0x850, NULL, CS_MAC_CELTIC },
|
||||
{ smRoman, verWelsh, 0, NULL, CS_MAC_CELTIC_OLD },
|
||||
{ smRoman, verIrishGaelicScript, 0x850, NULL, CS_MAC_GAELIC },
|
||||
{ smRoman, verIrishGaelicScript, 0, NULL, CS_MAC_GAELIC_OLD },
|
||||
#endif
|
||||
{ smRoman, verGreece, 0x922, NULL, CS_MAC_GREEK },
|
||||
{ smRoman, verGreece, 0, NULL, CS_MAC_GREEK_OLD },
|
||||
{ smRoman, -1, 0x850, NULL, CS_MAC_ROMAN },
|
||||
{ smRoman, -1, 0, NULL, CS_MAC_ROMAN_OLD },
|
||||
#if 0 /* Multi-byte encodings, not yet supported */
|
||||
{ smJapanese, -1, 0, NULL, CS_MAC_JAPANESE },
|
||||
{ smTradChinese, -1, 0, NULL, CS_MAC_CHINTRAD },
|
||||
{ smKorean, -1, 0, NULL, CS_MAC_KOREAN },
|
||||
#endif
|
||||
#if 0 /* Bidirectional encodings, not yet supported */
|
||||
{ smArabic, verIran, 0, NULL, CS_MAC_FARSI },
|
||||
{ smArabic, -1, 0, NULL, CS_MAC_ARABIC },
|
||||
{ smHebrew, -1, 0, NULL, CS_MAC_HEBREW },
|
||||
#endif
|
||||
{ smCyrillic, -1, 0x900, NULL, CS_MAC_CYRILLIC },
|
||||
{ smCyrillic, verRussia, 0, NULL, CS_MAC_CYRILLIC_OLD },
|
||||
{ smCyrillic, verBulgaria, 0, NULL, CS_MAC_CYRILLIC_OLD },
|
||||
{ smCyrillic, -1, 0, NULL, CS_MAC_UKRAINE },
|
||||
#if 0 /* Complex Indic scripts, not yet supported */
|
||||
{ smDevanagari, -1, 0, NULL, CS_MAC_DEVENAGA },
|
||||
{ smGurmukhi, -1, 0, NULL, CS_MAC_GURMUKHI },
|
||||
{ smGujurati, -1, 0, NULL, CS_MAC_GUJURATI },
|
||||
#endif
|
||||
{ smThai, -1, 0, NULL, CS_MAC_THAI },
|
||||
#if 0 /* Multi-byte encoding, not yet supported */
|
||||
{ smSimpChinese, -1, 0, NULL, CS_MAC_CHINSIMP },
|
||||
#endif
|
||||
#if 0 /* No mapping table on ftp.unicode.org */
|
||||
{ smTibetan, -1, 0, NULL, CS_MAC_TIBETAN },
|
||||
{ smEthiopic, -1, 0, NULL, CS_MAC_ETHIOPIC },
|
||||
{ smEthiopic, verNanavut, 0, NULL, CS_MAC_INUIT },
|
||||
#endif
|
||||
{ smCentralEuroRoman, -1, 0, NULL, CS_MAC_CENTEURO },
|
||||
};
|
||||
|
||||
int charset_from_macenc(int script, int region, int sysvers,
|
||||
char const *fontname)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)lenof(macencs); i++)
|
||||
if ((macencs[i].script == script) &&
|
||||
(macencs[i].region < 0 || macencs[i].region == region) &&
|
||||
(macencs[i].sysvermin <= sysvers) &&
|
||||
(macencs[i].fontname == NULL ||
|
||||
(fontname != NULL && strcmp(macencs[i].fontname, fontname) == 0)))
|
||||
return macencs[i].charset;
|
||||
|
||||
return CS_NONE;
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* mimeenc.c - translate our internal character set codes to and
|
||||
* from MIME standard character-set names.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include "charset.h"
|
||||
#include "internal.h"
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
int charset;
|
||||
} mimeencs[] = {
|
||||
/*
|
||||
* These names are taken from
|
||||
*
|
||||
* http://www.iana.org/assignments/character-sets
|
||||
*
|
||||
* Where multiple encoding names map to the same encoding id
|
||||
* (such as the variety of aliases for ISO-8859-1), the first
|
||||
* is considered canonical and will be returned when
|
||||
* translating the id to a string.
|
||||
*/
|
||||
{ "ISO-8859-1", CS_ISO8859_1 },
|
||||
{ "iso-ir-100", CS_ISO8859_1 },
|
||||
{ "ISO_8859-1", CS_ISO8859_1 },
|
||||
{ "ISO_8859-1:1987", CS_ISO8859_1 },
|
||||
{ "latin1", CS_ISO8859_1 },
|
||||
{ "l1", CS_ISO8859_1 },
|
||||
{ "IBM819", CS_ISO8859_1 },
|
||||
{ "CP819", CS_ISO8859_1 },
|
||||
{ "csISOLatin1", CS_ISO8859_1 },
|
||||
|
||||
{ "ISO-8859-2", CS_ISO8859_2 },
|
||||
{ "ISO_8859-2:1987", CS_ISO8859_2 },
|
||||
{ "iso-ir-101", CS_ISO8859_2 },
|
||||
{ "ISO_8859-2", CS_ISO8859_2 },
|
||||
{ "latin2", CS_ISO8859_2 },
|
||||
{ "l2", CS_ISO8859_2 },
|
||||
{ "csISOLatin2", CS_ISO8859_2 },
|
||||
|
||||
{ "ISO-8859-3", CS_ISO8859_3 },
|
||||
{ "ISO_8859-3:1988", CS_ISO8859_3 },
|
||||
{ "iso-ir-109", CS_ISO8859_3 },
|
||||
{ "ISO_8859-3", CS_ISO8859_3 },
|
||||
{ "latin3", CS_ISO8859_3 },
|
||||
{ "l3", CS_ISO8859_3 },
|
||||
{ "csISOLatin3", CS_ISO8859_3 },
|
||||
|
||||
{ "ISO-8859-4", CS_ISO8859_4 },
|
||||
{ "ISO_8859-4:1988", CS_ISO8859_4 },
|
||||
{ "iso-ir-110", CS_ISO8859_4 },
|
||||
{ "ISO_8859-4", CS_ISO8859_4 },
|
||||
{ "latin4", CS_ISO8859_4 },
|
||||
{ "l4", CS_ISO8859_4 },
|
||||
{ "csISOLatin4", CS_ISO8859_4 },
|
||||
|
||||
{ "ISO-8859-5", CS_ISO8859_5 },
|
||||
{ "ISO_8859-5:1988", CS_ISO8859_5 },
|
||||
{ "iso-ir-144", CS_ISO8859_5 },
|
||||
{ "ISO_8859-5", CS_ISO8859_5 },
|
||||
{ "cyrillic", CS_ISO8859_5 },
|
||||
{ "csISOLatinCyrillic", CS_ISO8859_5 },
|
||||
|
||||
{ "ISO-8859-6", CS_ISO8859_6 },
|
||||
{ "ISO_8859-6:1987", CS_ISO8859_6 },
|
||||
{ "iso-ir-127", CS_ISO8859_6 },
|
||||
{ "ISO_8859-6", CS_ISO8859_6 },
|
||||
{ "ECMA-114", CS_ISO8859_6 },
|
||||
{ "ASMO-708", CS_ISO8859_6 },
|
||||
{ "arabic", CS_ISO8859_6 },
|
||||
{ "csISOLatinArabic", CS_ISO8859_6 },
|
||||
|
||||
{ "ISO-8859-7", CS_ISO8859_7 },
|
||||
{ "ISO_8859-7:1987", CS_ISO8859_7 },
|
||||
{ "iso-ir-126", CS_ISO8859_7 },
|
||||
{ "ISO_8859-7", CS_ISO8859_7 },
|
||||
{ "ELOT_928", CS_ISO8859_7 },
|
||||
{ "ECMA-118", CS_ISO8859_7 },
|
||||
{ "greek", CS_ISO8859_7 },
|
||||
{ "greek8", CS_ISO8859_7 },
|
||||
{ "csISOLatinGreek", CS_ISO8859_7 },
|
||||
|
||||
{ "ISO-8859-8", CS_ISO8859_8 },
|
||||
{ "ISO_8859-8:1988", CS_ISO8859_8 },
|
||||
{ "iso-ir-138", CS_ISO8859_8 },
|
||||
{ "ISO_8859-8", CS_ISO8859_8 },
|
||||
{ "hebrew", CS_ISO8859_8 },
|
||||
{ "csISOLatinHebrew", CS_ISO8859_8 },
|
||||
|
||||
{ "ISO-8859-9", CS_ISO8859_9 },
|
||||
{ "ISO_8859-9:1989", CS_ISO8859_9 },
|
||||
{ "iso-ir-148", CS_ISO8859_9 },
|
||||
{ "ISO_8859-9", CS_ISO8859_9 },
|
||||
{ "latin5", CS_ISO8859_9 },
|
||||
{ "l5", CS_ISO8859_9 },
|
||||
{ "csISOLatin5", CS_ISO8859_9 },
|
||||
|
||||
{ "ISO-8859-10", CS_ISO8859_10 },
|
||||
{ "iso-ir-157", CS_ISO8859_10 },
|
||||
{ "l6", CS_ISO8859_10 },
|
||||
{ "ISO_8859-10:1992", CS_ISO8859_10 },
|
||||
{ "csISOLatin6", CS_ISO8859_10 },
|
||||
{ "latin6", CS_ISO8859_10 },
|
||||
|
||||
{ "ISO-8859-13", CS_ISO8859_13 },
|
||||
|
||||
{ "ISO-8859-14", CS_ISO8859_14 },
|
||||
{ "iso-ir-199", CS_ISO8859_14 },
|
||||
{ "ISO_8859-14:1998", CS_ISO8859_14 },
|
||||
{ "ISO_8859-14", CS_ISO8859_14 },
|
||||
{ "latin8", CS_ISO8859_14 },
|
||||
{ "iso-celtic", CS_ISO8859_14 },
|
||||
{ "l8", CS_ISO8859_14 },
|
||||
|
||||
{ "ISO-8859-15", CS_ISO8859_15 },
|
||||
{ "ISO_8859-15", CS_ISO8859_15 },
|
||||
{ "Latin-9", CS_ISO8859_15 },
|
||||
|
||||
{ "ISO-8859-16", CS_ISO8859_16 },
|
||||
{ "iso-ir-226", CS_ISO8859_16 },
|
||||
{ "ISO_8859-16", CS_ISO8859_16 },
|
||||
{ "ISO_8859-16:2001", CS_ISO8859_16 },
|
||||
{ "latin10", CS_ISO8859_16 },
|
||||
{ "l10", CS_ISO8859_16 },
|
||||
|
||||
{ "IBM437", CS_CP437 },
|
||||
{ "cp437", CS_CP437 },
|
||||
{ "437", CS_CP437 },
|
||||
{ "csPC8CodePage437", CS_CP437 },
|
||||
|
||||
{ "IBM850", CS_CP850 },
|
||||
{ "cp850", CS_CP850 },
|
||||
{ "850", CS_CP850 },
|
||||
{ "csPC850Multilingual", CS_CP850 },
|
||||
|
||||
{ "IBM852", CS_CP852 },
|
||||
{ "cp852", CS_CP852 },
|
||||
{ "852", CS_CP852 },
|
||||
{ "csIBM852", CS_CP852 },
|
||||
|
||||
{ "IBM866", CS_CP866 },
|
||||
{ "cp866", CS_CP866 },
|
||||
{ "866", CS_CP866 },
|
||||
{ "csIBM866", CS_CP866 },
|
||||
|
||||
{ "windows-1250", CS_CP1250 },
|
||||
|
||||
{ "windows-1251", CS_CP1251 },
|
||||
|
||||
{ "windows-1252", CS_CP1252 },
|
||||
|
||||
{ "windows-1253", CS_CP1253 },
|
||||
|
||||
{ "windows-1254", CS_CP1254 },
|
||||
|
||||
{ "windows-1255", CS_CP1255 },
|
||||
|
||||
{ "windows-1256", CS_CP1256 },
|
||||
|
||||
{ "windows-1257", CS_CP1257 },
|
||||
|
||||
{ "windows-1258", CS_CP1258 },
|
||||
|
||||
{ "KOI8-R", CS_KOI8_R },
|
||||
{ "csKOI8R", CS_KOI8_R },
|
||||
|
||||
{ "KOI8-U", CS_KOI8_U },
|
||||
|
||||
{ "macintosh", CS_MAC_ROMAN_OLD },
|
||||
{ "mac", CS_MAC_ROMAN_OLD },
|
||||
{ "csMacintosh", CS_MAC_ROMAN_OLD },
|
||||
|
||||
{ "VISCII", CS_VISCII },
|
||||
{ "csVISCII", CS_VISCII },
|
||||
|
||||
{ "hp-roman8", CS_HP_ROMAN8 },
|
||||
{ "roman8", CS_HP_ROMAN8 },
|
||||
{ "r8", CS_HP_ROMAN8 },
|
||||
{ "csHPRoman8", CS_HP_ROMAN8 },
|
||||
|
||||
{ "DEC-MCS", CS_DEC_MCS },
|
||||
{ "dec", CS_DEC_MCS },
|
||||
{ "csDECMCS", CS_DEC_MCS },
|
||||
|
||||
{ "UTF-8", CS_UTF8 },
|
||||
};
|
||||
|
||||
const char *charset_to_mimeenc(int charset)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)lenof(mimeencs); i++)
|
||||
if (charset == mimeencs[i].charset)
|
||||
return mimeencs[i].name;
|
||||
|
||||
return NULL; /* not found */
|
||||
}
|
||||
|
||||
int charset_from_mimeenc(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)lenof(mimeencs); i++) {
|
||||
const char *p, *q;
|
||||
p = name;
|
||||
q = mimeencs[i].name;
|
||||
while (*p || *q) {
|
||||
if (tolower((unsigned char)*p) != tolower((unsigned char)*q))
|
||||
break;
|
||||
p++; q++;
|
||||
}
|
||||
if (!*p && !*q)
|
||||
return mimeencs[i].charset;
|
||||
}
|
||||
|
||||
return CS_NONE; /* not found */
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* sbcs.c - routines to handle single-byte character sets.
|
||||
*/
|
||||
|
||||
#include "charset.h"
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* The charset_spec for any single-byte character set should
|
||||
* provide read_sbcs() as its read function, and its `data' field
|
||||
* should be a wchar_t string constant containing the 256 entries
|
||||
* of the translation table.
|
||||
*/
|
||||
|
||||
void read_sbcs(charset_spec const *charset, long int input_chr,
|
||||
charset_state *state,
|
||||
void (*emit)(void *ctx, long int output), void *emitctx)
|
||||
{
|
||||
const struct sbcs_data *sd = charset->data;
|
||||
|
||||
UNUSEDARG(state);
|
||||
|
||||
emit(emitctx, sd->sbcs2ucs[input_chr]);
|
||||
}
|
||||
|
||||
void write_sbcs(charset_spec const *charset, long int input_chr,
|
||||
charset_state *state,
|
||||
void (*emit)(void *ctx, long int output), void *emitctx)
|
||||
{
|
||||
const struct sbcs_data *sd = charset->data;
|
||||
int i, j, k, c;
|
||||
|
||||
UNUSEDARG(state);
|
||||
|
||||
/*
|
||||
* Binary-search in the ucs2sbcs table.
|
||||
*/
|
||||
i = -1;
|
||||
j = sd->nvalid;
|
||||
while (i+1 < j) {
|
||||
k = (i+j)/2;
|
||||
c = sd->ucs2sbcs[k];
|
||||
if (input_chr < sd->sbcs2ucs[c])
|
||||
j = k;
|
||||
else if (input_chr > sd->sbcs2ucs[c])
|
||||
i = k;
|
||||
else {
|
||||
emit(emitctx, c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
emit(emitctx, ERROR);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env perl -w
|
||||
|
||||
# This script generates sbcsdat.c (the data for all the SBCSes) from its
|
||||
# source form sbcs.dat.
|
||||
|
||||
$infile = "sbcs.dat";
|
||||
$outfile = "sbcsdat.c";
|
||||
|
||||
open FOO, $infile;
|
||||
open BAR, ">$outfile";
|
||||
select BAR;
|
||||
|
||||
print "/*\n";
|
||||
print " * sbcsdat.c - data definitions for single-byte character sets.\n";
|
||||
print " *\n";
|
||||
print " * Generated by sbcsgen.pl from sbcs.dat.\n";
|
||||
print " * You should edit those files rather than editing this one.\n";
|
||||
print " */\n";
|
||||
print "\n";
|
||||
print "#ifndef ENUM_CHARSETS\n";
|
||||
print "\n";
|
||||
print "#include \"charset.h\"\n";
|
||||
print "#include \"internal.h\"\n";
|
||||
print "\n";
|
||||
|
||||
my $charsetname = undef;
|
||||
my @vals = ();
|
||||
|
||||
my @charsetnames = ();
|
||||
my @sortpriority = ();
|
||||
|
||||
while (<FOO>) {
|
||||
chomp;
|
||||
if (/^charset (.*)$/) {
|
||||
$charsetname = $1;
|
||||
@vals = ();
|
||||
@sortpriority = map { 0 } 0..255;
|
||||
} elsif (/^sortpriority ([^-]*)-([^-]*) (.*)$/) {
|
||||
for ($i = hex $1; $i <= hex $2; $i++) {
|
||||
$sortpriority[$i] += $3;
|
||||
}
|
||||
} elsif (/^[0-9a-fA-FX]/) {
|
||||
push @vals, map { $_ eq "XXXX" ? -1 : hex $_ } split / +/, $_;
|
||||
if (scalar @vals > 256) {
|
||||
die "$infile:$.: charset $charsetname has more than 256 values\n";
|
||||
} elsif (scalar @vals == 256) {
|
||||
&outcharset($charsetname, \@vals, \@sortpriority);
|
||||
push @charsetnames, $charsetname;
|
||||
$charsetname = undef;
|
||||
@vals = ();
|
||||
@sortpriority = map { 0 } 0..255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print "#else /* ENUM_CHARSETS */\n";
|
||||
print "\n";
|
||||
|
||||
foreach $i (@charsetnames) {
|
||||
print "ENUM_CHARSET($i)\n";
|
||||
}
|
||||
|
||||
print "\n";
|
||||
print "#endif /* ENUM_CHARSETS */\n";
|
||||
|
||||
sub outcharset($$$) {
|
||||
my ($name, $vals, $sortpriority) = @_;
|
||||
my ($prefix, $i, @sorted);
|
||||
|
||||
print "static const sbcs_data data_$name = {\n";
|
||||
print " {\n";
|
||||
$prefix = " ";
|
||||
@sorted = ();
|
||||
for ($i = 0; $i < 256; $i++) {
|
||||
if ($vals->[$i] < 0) {
|
||||
printf "%sERROR ", $prefix;
|
||||
} else {
|
||||
printf "%s0x%04x", $prefix, $vals->[$i];
|
||||
die "ooh? $i\n" unless defined $sortpriority->[$i];
|
||||
push @sorted, [$i, $vals->[$i], 0+$sortpriority->[$i]];
|
||||
}
|
||||
if ($i % 8 == 7) {
|
||||
$prefix = ",\n ";
|
||||
} else {
|
||||
$prefix = ", ";
|
||||
}
|
||||
}
|
||||
print "\n },\n {\n";
|
||||
@sorted = sort { ($a->[1] == $b->[1] ?
|
||||
$b->[2] <=> $a->[2] :
|
||||
$a->[1] <=> $b->[1]) ||
|
||||
$a->[0] <=> $b->[0] } @sorted;
|
||||
$prefix = " ";
|
||||
$uval = -1;
|
||||
for ($i = $j = 0; $i < scalar @sorted; $i++) {
|
||||
next if ($uval == $sorted[$i]->[1]); # low-priority alternative
|
||||
$uval = $sorted[$i]->[1];
|
||||
printf "%s0x%02x", $prefix, $sorted[$i]->[0];
|
||||
if ($j % 8 == 7) {
|
||||
$prefix = ",\n ";
|
||||
} else {
|
||||
$prefix = ", ";
|
||||
}
|
||||
$j++;
|
||||
}
|
||||
printf "\n },\n %d\n", $j;
|
||||
print "};\n";
|
||||
print "const charset_spec charset_$name = {\n" .
|
||||
" $name, read_sbcs, write_sbcs, &data_$name\n};\n\n";
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* slookup.c - static lookup of character sets.
|
||||
*/
|
||||
|
||||
#include "charset.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define ENUM_CHARSET(x) extern charset_spec const charset_##x;
|
||||
#include "enum.c"
|
||||
#undef ENUM_CHARSET
|
||||
|
||||
static charset_spec const *const cs_table[] = {
|
||||
|
||||
#define ENUM_CHARSET(x) &charset_##x,
|
||||
#include "enum.c"
|
||||
#undef ENUM_CHARSET
|
||||
|
||||
};
|
||||
|
||||
charset_spec const *charset_find_spec(int charset)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)lenof(cs_table); i++)
|
||||
if (cs_table[i]->charset == charset)
|
||||
return cs_table[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* toucs.c - convert charsets to Unicode.
|
||||
*/
|
||||
|
||||
#include "charset.h"
|
||||
#include "internal.h"
|
||||
|
||||
struct unicode_emit_param {
|
||||
wchar_t *output;
|
||||
int outlen;
|
||||
const wchar_t *errstr;
|
||||
int errlen;
|
||||
int stopped;
|
||||
};
|
||||
|
||||
static void unicode_emit(void *ctx, long int output)
|
||||
{
|
||||
struct unicode_emit_param *param = (struct unicode_emit_param *)ctx;
|
||||
wchar_t outval;
|
||||
wchar_t const *p;
|
||||
int outlen;
|
||||
|
||||
if (output == ERROR) {
|
||||
if (param->errstr) {
|
||||
p = param->errstr;
|
||||
outlen = param->errlen;
|
||||
} else {
|
||||
outval = 0xFFFD; /* U+FFFD REPLACEMENT CHARACTER */
|
||||
p = &outval;
|
||||
outlen = 1;
|
||||
}
|
||||
} else {
|
||||
outval = output;
|
||||
p = &outval;
|
||||
outlen = 1;
|
||||
}
|
||||
|
||||
if (param->outlen >= outlen) {
|
||||
while (outlen > 0) {
|
||||
*param->output++ = *p++;
|
||||
param->outlen--;
|
||||
outlen--;
|
||||
}
|
||||
} else {
|
||||
param->stopped = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int charset_to_unicode(const char **input, int *inlen,
|
||||
wchar_t *output, int outlen,
|
||||
int charset, charset_state *state,
|
||||
const wchar_t *errstr, int errlen)
|
||||
{
|
||||
charset_spec const *spec = charset_find_spec(charset);
|
||||
charset_state localstate;
|
||||
struct unicode_emit_param param;
|
||||
|
||||
param.output = output;
|
||||
param.outlen = outlen;
|
||||
param.errstr = errstr;
|
||||
param.errlen = errlen;
|
||||
param.stopped = 0;
|
||||
|
||||
if (!state) {
|
||||
localstate.s0 = 0;
|
||||
} else {
|
||||
localstate = *state; /* structure copy */
|
||||
}
|
||||
|
||||
while (*inlen > 0) {
|
||||
int lenbefore = param.output - output;
|
||||
spec->read(spec, (unsigned char)**input, &localstate,
|
||||
unicode_emit, ¶m);
|
||||
if (param.stopped) {
|
||||
/*
|
||||
* The emit function has _tried_ to output some
|
||||
* characters, but ran up against the end of the
|
||||
* buffer. Leave immediately, and return what happened
|
||||
* _before_ attempting to process this character.
|
||||
*/
|
||||
return lenbefore;
|
||||
}
|
||||
if (state)
|
||||
*state = localstate; /* structure copy */
|
||||
(*input)++;
|
||||
(*inlen)--;
|
||||
}
|
||||
|
||||
return param.output - output;
|
||||
}
|
||||
@@ -0,0 +1,877 @@
|
||||
/*
|
||||
* utf8.c - routines to handle UTF-8.
|
||||
*/
|
||||
|
||||
#ifndef ENUM_CHARSETS
|
||||
|
||||
#include "charset.h"
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* UTF-8 has no associated data, so `charset' may be ignored.
|
||||
*/
|
||||
|
||||
static void read_utf8(charset_spec const *charset, long int input_chr,
|
||||
charset_state *state,
|
||||
void (*emit)(void *ctx, long int output), void *emitctx)
|
||||
{
|
||||
UNUSEDARG(charset);
|
||||
|
||||
/*
|
||||
* For reading UTF-8, the `state' word contains:
|
||||
*
|
||||
* - in bits 29-31, the number of bytes expected to be in the
|
||||
* current multibyte character (which we can tell instantly
|
||||
* from the first byte, of course).
|
||||
*
|
||||
* - in bits 26-28, the number of bytes _seen so far_ in the
|
||||
* current multibyte character.
|
||||
*
|
||||
* - in the remainder of the word, the current value of the
|
||||
* character, which is shifted upwards by 6 bits to
|
||||
* accommodate each new byte.
|
||||
*
|
||||
* As required, the state is zero when we are not in the middle
|
||||
* of a multibyte character at all.
|
||||
*
|
||||
* For example, when reading E9 8D 8B, starting at state=0:
|
||||
*
|
||||
* - after E9, the state is 0x64000009
|
||||
* - after 8D, the state is 0x6800024d
|
||||
* - after 8B, the state conceptually becomes 0x6c00934b, at
|
||||
* which point we notice we've got as many characters as we
|
||||
* were expecting, output U+934B, and reset the state to
|
||||
* zero.
|
||||
*
|
||||
* Note that the maximum number of bits we might need to store
|
||||
* in the character value field is 25 (U+7FFFFFFF contains 31
|
||||
* bits, but we will never actually store its full value
|
||||
* because when we receive the last 6 bits in the final
|
||||
* continuation byte we will output it and revert the state to
|
||||
* zero). Hence the character value field never collides with
|
||||
* the byte counts.
|
||||
*/
|
||||
|
||||
if (input_chr < 0x80) {
|
||||
/*
|
||||
* Single-byte character. If the state is nonzero before
|
||||
* coming here, output an error for an incomplete sequence.
|
||||
* Then output the character.
|
||||
*/
|
||||
if (state->s0 != 0) {
|
||||
emit(emitctx, ERROR);
|
||||
state->s0 = 0;
|
||||
}
|
||||
emit(emitctx, input_chr);
|
||||
} else if (input_chr == 0xFE || input_chr == 0xFF) {
|
||||
/*
|
||||
* FE and FF bytes should _never_ occur in UTF-8. They are
|
||||
* automatic errors; if the state was nonzero to start
|
||||
* with, output a further error for an incomplete sequence.
|
||||
*/
|
||||
if (state->s0 != 0) {
|
||||
emit(emitctx, ERROR);
|
||||
state->s0 = 0;
|
||||
}
|
||||
emit(emitctx, ERROR);
|
||||
} else if (input_chr >= 0x80 && input_chr < 0xC0) {
|
||||
/*
|
||||
* Continuation byte. Output an error for an unexpected
|
||||
* continuation byte, if the state is zero.
|
||||
*/
|
||||
if (state->s0 == 0) {
|
||||
emit(emitctx, ERROR);
|
||||
} else {
|
||||
unsigned long charval;
|
||||
unsigned long topstuff;
|
||||
int bytes;
|
||||
|
||||
/*
|
||||
* Otherwise, accumulate more of the character value.
|
||||
*/
|
||||
charval = state->s0 & 0x03ffffffL;
|
||||
charval = (charval << 6) | (input_chr & 0x3F);
|
||||
|
||||
/*
|
||||
* Check the byte counts; if we have not reached the
|
||||
* end of the character, update the state and return.
|
||||
*/
|
||||
topstuff = state->s0 & 0xfc000000L;
|
||||
topstuff += 0x04000000L; /* add one to the byte count */
|
||||
if (((topstuff << 3) ^ topstuff) & 0xe0000000L) {
|
||||
state->s0 = topstuff | charval;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we know we've reached the end of the character.
|
||||
* `charval' is the Unicode value. We should check for
|
||||
* various invalid things, and then either output
|
||||
* charval or an error. In all cases we reset the state
|
||||
* to zero.
|
||||
*/
|
||||
bytes = topstuff >> 29;
|
||||
state->s0 = 0;
|
||||
|
||||
if (charval >= 0xD800 && charval < 0xE000) {
|
||||
/*
|
||||
* Surrogates (0xD800-0xDFFF) may never be encoded
|
||||
* in UTF-8. A surrogate pair in Unicode should
|
||||
* have been encoded as a single UTF-8 character
|
||||
* occupying more than three bytes.
|
||||
*/
|
||||
emit(emitctx, ERROR);
|
||||
} else if (charval == 0xFFFE || charval == 0xFFFF) {
|
||||
/*
|
||||
* U+FFFE and U+FFFF are invalid Unicode characters
|
||||
* and may never be encoded in UTF-8. (This is one
|
||||
* reason why U+FFFF is our way of signalling an
|
||||
* error to our `emit' function :-)
|
||||
*/
|
||||
emit(emitctx, ERROR);
|
||||
} else if ((charval <= 0x7FL /* && bytes > 1 */) ||
|
||||
(charval <= 0x7FFL && bytes > 2) ||
|
||||
(charval <= 0xFFFFL && bytes > 3) ||
|
||||
(charval <= 0x1FFFFFL && bytes > 4) ||
|
||||
(charval <= 0x3FFFFFFL && bytes > 5)) {
|
||||
/*
|
||||
* Overlong sequences are not to be tolerated,
|
||||
* under any circumstances.
|
||||
*/
|
||||
emit(emitctx, ERROR);
|
||||
} else {
|
||||
/*
|
||||
* Oh, all right. We'll let this one off.
|
||||
*/
|
||||
emit(emitctx, charval);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* Lead byte. First output an error for an incomplete
|
||||
* sequence, if the state is nonzero.
|
||||
*/
|
||||
if (state->s0 != 0)
|
||||
emit(emitctx, ERROR);
|
||||
|
||||
/*
|
||||
* Now deal with the lead byte: work out the number of
|
||||
* bytes we expect to see in this character, and extract
|
||||
* the initial bits of it too.
|
||||
*/
|
||||
if (input_chr >= 0xC0 && input_chr < 0xE0) {
|
||||
state->s0 = 0x44000000L | (input_chr & 0x1F);
|
||||
} else if (input_chr >= 0xE0 && input_chr < 0xF0) {
|
||||
state->s0 = 0x64000000L | (input_chr & 0x0F);
|
||||
} else if (input_chr >= 0xF0 && input_chr < 0xF8) {
|
||||
state->s0 = 0x84000000L | (input_chr & 0x07);
|
||||
} else if (input_chr >= 0xF8 && input_chr < 0xFC) {
|
||||
state->s0 = 0xa4000000L | (input_chr & 0x03);
|
||||
} else if (input_chr >= 0xFC && input_chr < 0xFE) {
|
||||
state->s0 = 0xc4000000L | (input_chr & 0x01);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* UTF-8 is a stateless multi-byte encoding (in the sense that just
|
||||
* after any character has been completed, the state is always the
|
||||
* same); hence when writing it, there is no need to use the
|
||||
* charset_state.
|
||||
*/
|
||||
|
||||
static void write_utf8(charset_spec const *charset, long int input_chr,
|
||||
charset_state *state,
|
||||
void (*emit)(void *ctx, long int output), void *emitctx)
|
||||
{
|
||||
UNUSEDARG(charset);
|
||||
UNUSEDARG(state);
|
||||
|
||||
/*
|
||||
* Refuse to output any illegal code points.
|
||||
*/
|
||||
if (input_chr == 0xFFFE || input_chr == 0xFFFF ||
|
||||
(input_chr >= 0xD800 && input_chr < 0xE000)) {
|
||||
emit(emitctx, ERROR);
|
||||
} else if (input_chr < 0x80) { /* one-byte character */
|
||||
emit(emitctx, input_chr);
|
||||
} else if (input_chr < 0x800) { /* two-byte character */
|
||||
emit(emitctx, 0xC0 | (0x1F & (input_chr >> 6)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr )));
|
||||
} else if (input_chr < 0x10000) { /* three-byte character */
|
||||
emit(emitctx, 0xE0 | (0x0F & (input_chr >> 12)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr >> 6)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr )));
|
||||
} else if (input_chr < 0x200000) { /* four-byte character */
|
||||
emit(emitctx, 0xF0 | (0x07 & (input_chr >> 18)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr >> 12)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr >> 6)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr )));
|
||||
} else if (input_chr < 0x4000000) {/* five-byte character */
|
||||
emit(emitctx, 0xF8 | (0x03 & (input_chr >> 24)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr >> 18)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr >> 12)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr >> 6)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr )));
|
||||
} else { /* six-byte character */
|
||||
emit(emitctx, 0xFC | (0x01 & (input_chr >> 30)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr >> 24)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr >> 18)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr >> 12)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr >> 6)));
|
||||
emit(emitctx, 0x80 | (0x3F & (input_chr )));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TESTMODE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
int total_errs = 0;
|
||||
|
||||
void utf8_emit(void *ctx, long output)
|
||||
{
|
||||
wchar_t **p = (wchar_t **)ctx;
|
||||
*(*p)++ = output;
|
||||
}
|
||||
|
||||
void utf8_read_test(int line, char *input, int inlen, ...)
|
||||
{
|
||||
va_list ap;
|
||||
wchar_t *p, str[512];
|
||||
int i;
|
||||
charset_state state;
|
||||
unsigned long l;
|
||||
|
||||
state.s0 = 0;
|
||||
p = str;
|
||||
|
||||
for (i = 0; i < inlen; i++)
|
||||
read_utf8(NULL, input[i] & 0xFF, &state, utf8_emit, &p);
|
||||
|
||||
va_start(ap, inlen);
|
||||
l = 0;
|
||||
for (i = 0; i < p - str; i++) {
|
||||
l = va_arg(ap, long int);
|
||||
if (l == -1) {
|
||||
printf("%d: correct string shorter than output\n", line);
|
||||
total_errs++;
|
||||
break;
|
||||
}
|
||||
if (l != str[i]) {
|
||||
printf("%d: char %d came out as %08x, should be %08x\n",
|
||||
line, i, str[i], (unsigned)l);
|
||||
total_errs++;
|
||||
}
|
||||
}
|
||||
if (l != -1) {
|
||||
l = va_arg(ap, long int);
|
||||
if (l != -1) {
|
||||
printf("%d: correct string longer than output\n", line);
|
||||
total_errs++;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void utf8_write_test(int line, const long *input, int inlen, ...)
|
||||
{
|
||||
va_list ap;
|
||||
wchar_t *p, str[512];
|
||||
int i;
|
||||
charset_state state;
|
||||
unsigned long l;
|
||||
|
||||
state.s0 = 0;
|
||||
p = str;
|
||||
|
||||
for (i = 0; i < inlen; i++)
|
||||
write_utf8(NULL, input[i], &state, utf8_emit, &p);
|
||||
|
||||
va_start(ap, inlen);
|
||||
l = 0;
|
||||
for (i = 0; i < p - str; i++) {
|
||||
l = va_arg(ap, long int);
|
||||
if (l == -1) {
|
||||
printf("%d: correct string shorter than output\n", line);
|
||||
total_errs++;
|
||||
break;
|
||||
}
|
||||
if (l != str[i]) {
|
||||
printf("%d: char %d came out as %08x, should be %08x\n",
|
||||
line, i, str[i], (unsigned)l);
|
||||
total_errs++;
|
||||
}
|
||||
}
|
||||
if (l != -1) {
|
||||
l = va_arg(ap, long int);
|
||||
if (l != -1) {
|
||||
printf("%d: correct string longer than output\n", line);
|
||||
total_errs++;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* Macro to concoct the first three parameters of utf8_read_test. */
|
||||
#define TESTSTR(x) __LINE__, x, lenof(x)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("read tests beginning\n");
|
||||
utf8_read_test(TESTSTR("\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5"),
|
||||
0x000003BA, /* GREEK SMALL LETTER KAPPA */
|
||||
0x00001F79, /* GREEK SMALL LETTER OMICRON WITH OXIA */
|
||||
0x000003C3, /* GREEK SMALL LETTER SIGMA */
|
||||
0x000003BC, /* GREEK SMALL LETTER MU */
|
||||
0x000003B5, /* GREEK SMALL LETTER EPSILON */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\x00"),
|
||||
0x00000000, /* <control> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xC2\x80"),
|
||||
0x00000080, /* <control> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xE0\xA0\x80"),
|
||||
0x00000800, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF0\x90\x80\x80"),
|
||||
0x00010000, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF8\x88\x80\x80\x80"),
|
||||
0x00200000, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFC\x84\x80\x80\x80\x80"),
|
||||
0x04000000, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\x7F"),
|
||||
0x0000007F, /* <control> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xDF\xBF"),
|
||||
0x000007FF, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xEF\xBF\xBD"),
|
||||
0x0000FFFD, /* REPLACEMENT CHARACTER */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xEF\xBF\xBF"),
|
||||
ERROR, /* <no name available> (invalid char) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF7\xBF\xBF\xBF"),
|
||||
0x001FFFFF, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFB\xBF\xBF\xBF\xBF"),
|
||||
0x03FFFFFF, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFD\xBF\xBF\xBF\xBF\xBF"),
|
||||
0x7FFFFFFF, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\x9F\xBF"),
|
||||
0x0000D7FF, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xEE\x80\x80"),
|
||||
0x0000E000, /* <Private Use, First> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xEF\xBF\xBD"),
|
||||
0x0000FFFD, /* REPLACEMENT CHARACTER */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF4\x8F\xBF\xBF"),
|
||||
0x0010FFFF, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF4\x90\x80\x80"),
|
||||
0x00110000, /* <no name available> */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\x80"),
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xBF"),
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\x80\xBF"),
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\x80\xBF\x80"),
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\x80\xBF\x80\xBF"),
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80"),
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80\xBF"),
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80\xBF\x80"),
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"),
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
ERROR, /* (unexpected continuation byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xC0\x20\xC1\x20\xC2\x20\xC3\x20\xC4\x20\xC5\x20\xC6\x20\xC7\x20"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xE0\x20\xE1\x20\xE2\x20\xE3\x20\xE4\x20\xE5\x20\xE6\x20\xE7\x20\xE8\x20\xE9\x20\xEA\x20\xEB\x20\xEC\x20\xED\x20\xEE\x20\xEF\x20"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF0\x20\xF1\x20\xF2\x20\xF3\x20\xF4\x20\xF5\x20\xF6\x20\xF7\x20"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF8\x20\xF9\x20\xFA\x20\xFB\x20"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFC\x20\xFD\x20"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0x00000020, /* SPACE */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xC0"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xE0\x80"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF0\x80\x80"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF8\x80\x80\x80"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xDF"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xEF\xBF"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF7\xBF\xBF"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFB\xBF\xBF\xBF"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFD\xBF\xBF\xBF\xBF"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF"),
|
||||
ERROR, /* (incomplete sequence) */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
ERROR, /* (incomplete sequence) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFE"),
|
||||
ERROR, /* (invalid UTF-8 byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFF"),
|
||||
ERROR, /* (invalid UTF-8 byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFE\xFE\xFF\xFF"),
|
||||
ERROR, /* (invalid UTF-8 byte) */
|
||||
ERROR, /* (invalid UTF-8 byte) */
|
||||
ERROR, /* (invalid UTF-8 byte) */
|
||||
ERROR, /* (invalid UTF-8 byte) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xC0\xAF"),
|
||||
ERROR, /* SOLIDUS (overlong form of 2F) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xE0\x80\xAF"),
|
||||
ERROR, /* SOLIDUS (overlong form of 2F) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF0\x80\x80\xAF"),
|
||||
ERROR, /* SOLIDUS (overlong form of 2F) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF8\x80\x80\x80\xAF"),
|
||||
ERROR, /* SOLIDUS (overlong form of 2F) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80\xAF"),
|
||||
ERROR, /* SOLIDUS (overlong form of 2F) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xC1\xBF"),
|
||||
ERROR, /* <control> (overlong form of 7F) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xE0\x9F\xBF"),
|
||||
ERROR, /* <no name available> (overlong form of DF BF) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF0\x8F\xBF\xBF"),
|
||||
ERROR, /* <no name available> (overlong form of EF BF BF) (invalid char) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF8\x87\xBF\xBF\xBF"),
|
||||
ERROR, /* <no name available> (overlong form of F7 BF BF BF) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFC\x83\xBF\xBF\xBF\xBF"),
|
||||
ERROR, /* <no name available> (overlong form of FB BF BF BF BF) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xC0\x80"),
|
||||
ERROR, /* <control> (overlong form of 00) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xE0\x80\x80"),
|
||||
ERROR, /* <control> (overlong form of 00) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF0\x80\x80\x80"),
|
||||
ERROR, /* <control> (overlong form of 00) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xF8\x80\x80\x80\x80"),
|
||||
ERROR, /* <control> (overlong form of 00) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80\x80"),
|
||||
ERROR, /* <control> (overlong form of 00) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xA0\x80"),
|
||||
ERROR, /* <Non Private Use High Surrogate, First> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xAD\xBF"),
|
||||
ERROR, /* <Non Private Use High Surrogate, Last> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xAE\x80"),
|
||||
ERROR, /* <Private Use High Surrogate, First> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xAF\xBF"),
|
||||
ERROR, /* <Private Use High Surrogate, Last> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xB0\x80"),
|
||||
ERROR, /* <Low Surrogate, First> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xBE\x80"),
|
||||
ERROR, /* <no name available> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xBF\xBF"),
|
||||
ERROR, /* <Low Surrogate, Last> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xA0\x80\xED\xB0\x80"),
|
||||
ERROR, /* <Non Private Use High Surrogate, First> (surrogate) */
|
||||
ERROR, /* <Low Surrogate, First> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xA0\x80\xED\xBF\xBF"),
|
||||
ERROR, /* <Non Private Use High Surrogate, First> (surrogate) */
|
||||
ERROR, /* <Low Surrogate, Last> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xAD\xBF\xED\xB0\x80"),
|
||||
ERROR, /* <Non Private Use High Surrogate, Last> (surrogate) */
|
||||
ERROR, /* <Low Surrogate, First> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xAD\xBF\xED\xBF\xBF"),
|
||||
ERROR, /* <Non Private Use High Surrogate, Last> (surrogate) */
|
||||
ERROR, /* <Low Surrogate, Last> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xAE\x80\xED\xB0\x80"),
|
||||
ERROR, /* <Private Use High Surrogate, First> (surrogate) */
|
||||
ERROR, /* <Low Surrogate, First> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xAE\x80\xED\xBF\xBF"),
|
||||
ERROR, /* <Private Use High Surrogate, First> (surrogate) */
|
||||
ERROR, /* <Low Surrogate, Last> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xAF\xBF\xED\xB0\x80"),
|
||||
ERROR, /* <Private Use High Surrogate, Last> (surrogate) */
|
||||
ERROR, /* <Low Surrogate, First> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xED\xAF\xBF\xED\xBF\xBF"),
|
||||
ERROR, /* <Private Use High Surrogate, Last> (surrogate) */
|
||||
ERROR, /* <Low Surrogate, Last> (surrogate) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xEF\xBF\xBE"),
|
||||
ERROR, /* <no name available> (invalid char) */
|
||||
0, -1);
|
||||
utf8_read_test(TESTSTR("\xEF\xBF\xBF"),
|
||||
ERROR, /* <no name available> (invalid char) */
|
||||
0, -1);
|
||||
printf("read tests completed\n");
|
||||
printf("write tests beginning\n");
|
||||
{
|
||||
const static long str[] =
|
||||
{0x03BAL, 0x1F79L, 0x03C3L, 0x03BCL, 0x03B5L, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xCE, 0xBA,
|
||||
0xE1, 0xBD, 0xB9,
|
||||
0xCF, 0x83,
|
||||
0xCE, 0xBC,
|
||||
0xCE, 0xB5,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x0000L, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0x00,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x0080L, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xC2, 0x80,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x0800L, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xE0, 0xA0, 0x80,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x00010000L, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xF0, 0x90, 0x80, 0x80,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x00200000L, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xF8, 0x88, 0x80, 0x80, 0x80,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x04000000L, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xFC, 0x84, 0x80, 0x80, 0x80, 0x80,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x007FL, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0x7F,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x07FFL, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xDF, 0xBF,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0xFFFDL, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xEF, 0xBF, 0xBD,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0xFFFFL, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
ERROR,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x001FFFFFL, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xF7, 0xBF, 0xBF, 0xBF,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x03FFFFFFL, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xFB, 0xBF, 0xBF, 0xBF, 0xBF,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0x7FFFFFFFL, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xFD, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0xD7FFL, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xED, 0x9F, 0xBF,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0xD800L, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
ERROR,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0xD800L, 0xDC00L, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
ERROR,
|
||||
ERROR,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0xDFFFL, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
ERROR,
|
||||
0, -1);
|
||||
}
|
||||
{
|
||||
const static long str[] = {0xE000L, 0};
|
||||
utf8_write_test(TESTSTR(str),
|
||||
0xEE, 0x80, 0x80,
|
||||
0, -1);
|
||||
}
|
||||
printf("write tests completed\n");
|
||||
|
||||
printf("total: %d errors\n", total_errs);
|
||||
return (total_errs != 0);
|
||||
}
|
||||
#endif /* TESTMODE */
|
||||
|
||||
const charset_spec charset_CS_UTF8 = {
|
||||
CS_UTF8, read_utf8, write_utf8, NULL
|
||||
};
|
||||
|
||||
#else /* ENUM_CHARSETS */
|
||||
|
||||
ENUM_CHARSET(CS_UTF8)
|
||||
|
||||
#endif /* ENUM_CHARSETS */
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* xenc.c - translate our internal character set codes to and from
|
||||
* X11 character encoding names.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include "charset.h"
|
||||
#include "internal.h"
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
int charset;
|
||||
} xencs[] = {
|
||||
/*
|
||||
* Officially registered encoding names. This list is derived
|
||||
* from the font encodings section of
|
||||
*
|
||||
* http://ftp.x.org/pub/DOCS/registry
|
||||
*
|
||||
* Where multiple encoding names map to the same encoding id
|
||||
* (such as iso8859-15 and fcd8859-15), the first is considered
|
||||
* canonical and will be returned when translating the id to a
|
||||
* string.
|
||||
*/
|
||||
{ "iso8859-1", CS_ISO8859_1 },
|
||||
{ "iso8859-2", CS_ISO8859_2 },
|
||||
{ "iso8859-3", CS_ISO8859_3 },
|
||||
{ "iso8859-4", CS_ISO8859_4 },
|
||||
{ "iso8859-5", CS_ISO8859_5 },
|
||||
{ "iso8859-6", CS_ISO8859_6 },
|
||||
{ "iso8859-7", CS_ISO8859_7 },
|
||||
{ "iso8859-8", CS_ISO8859_8 },
|
||||
{ "iso8859-9", CS_ISO8859_9 },
|
||||
{ "iso8859-10", CS_ISO8859_10 },
|
||||
{ "iso8859-13", CS_ISO8859_13 },
|
||||
{ "iso8859-14", CS_ISO8859_14 },
|
||||
{ "iso8859-15", CS_ISO8859_15 },
|
||||
{ "fcd8859-15", CS_ISO8859_15 },
|
||||
{ "hp-roman8", CS_HP_ROMAN8 },
|
||||
{ "koi8-r", CS_KOI8_R },
|
||||
/*
|
||||
* Unofficial encoding names found in the wild.
|
||||
*/
|
||||
{ "iso8859-16", CS_ISO8859_16 },
|
||||
{ "koi8-u", CS_KOI8_U },
|
||||
{ "ibm-cp437", CS_CP437 },
|
||||
{ "ibm-cp850", CS_CP850 },
|
||||
{ "ibm-cp852", CS_CP852 },
|
||||
{ "ibm-cp866", CS_CP866 },
|
||||
{ "microsoft-cp1250", CS_CP1250 },
|
||||
{ "microsoft-cp1251", CS_CP1251 },
|
||||
{ "microsoft-cp1252", CS_CP1252 },
|
||||
{ "microsoft-cp1253", CS_CP1253 },
|
||||
{ "microsoft-cp1254", CS_CP1254 },
|
||||
{ "microsoft-cp1255", CS_CP1255 },
|
||||
{ "microsoft-cp1256", CS_CP1256 },
|
||||
{ "microsoft-cp1257", CS_CP1257 },
|
||||
{ "microsoft-cp1258", CS_CP1258 },
|
||||
{ "mac-roman", CS_MAC_ROMAN },
|
||||
{ "viscii1.1-1", CS_VISCII },
|
||||
{ "viscii1-1", CS_VISCII },
|
||||
};
|
||||
|
||||
const char *charset_to_xenc(int charset)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)lenof(xencs); i++)
|
||||
if (charset == xencs[i].charset)
|
||||
return xencs[i].name;
|
||||
|
||||
return NULL; /* not found */
|
||||
}
|
||||
|
||||
int charset_from_xenc(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)lenof(xencs); i++) {
|
||||
const char *p, *q;
|
||||
p = name;
|
||||
q = xencs[i].name;
|
||||
while (*p || *q) {
|
||||
if (tolower((unsigned char)*p) != tolower((unsigned char)*q))
|
||||
break;
|
||||
p++; q++;
|
||||
}
|
||||
if (!*p && !*q)
|
||||
return xencs[i].charset;
|
||||
}
|
||||
|
||||
return CS_NONE; /* not found */
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* clicons.c: definitions limited to tools that link against both
|
||||
* console.c and cmdline.c.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
static const LogPolicyVtable console_cli_logpolicy_vt = {
|
||||
.eventlog = console_eventlog,
|
||||
.askappend = console_askappend,
|
||||
.logging_error = console_logging_error,
|
||||
.verbose = cmdline_lp_verbose,
|
||||
};
|
||||
LogPolicy console_cli_logpolicy[1] = {{ &console_cli_logpolicy_vt }};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,614 @@
|
||||
/*
|
||||
* conf.c: implementation of the internal storage format used for
|
||||
* the configuration of a PuTTY session.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "tree234.h"
|
||||
#include "putty.h"
|
||||
|
||||
/*
|
||||
* Enumeration of types used in keys and values.
|
||||
*/
|
||||
typedef enum {
|
||||
TYPE_NONE, TYPE_BOOL, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT
|
||||
} Type;
|
||||
|
||||
/*
|
||||
* Arrays which allow us to look up the subkey and value types for a
|
||||
* given primary key id.
|
||||
*/
|
||||
#define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype,
|
||||
static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) };
|
||||
#define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype,
|
||||
static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) };
|
||||
|
||||
/*
|
||||
* Configuration keys are primarily integers (big enum of all the
|
||||
* different configurable options); some keys have string-designated
|
||||
* subkeys, such as the list of environment variables (subkeys
|
||||
* defined by the variable names); some have integer-designated
|
||||
* subkeys (wordness, colours, preference lists).
|
||||
*/
|
||||
struct key {
|
||||
int primary;
|
||||
union {
|
||||
int i;
|
||||
char *s;
|
||||
} secondary;
|
||||
};
|
||||
|
||||
/* Variant form of struct key which doesn't contain dynamic data, used
|
||||
* for lookups. */
|
||||
struct constkey {
|
||||
int primary;
|
||||
union {
|
||||
int i;
|
||||
const char *s;
|
||||
} secondary;
|
||||
};
|
||||
|
||||
struct value {
|
||||
union {
|
||||
bool boolval;
|
||||
int intval;
|
||||
char *stringval;
|
||||
Filename *fileval;
|
||||
FontSpec *fontval;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct conf_entry {
|
||||
struct key key;
|
||||
struct value value;
|
||||
};
|
||||
|
||||
struct conf_tag {
|
||||
tree234 *tree;
|
||||
};
|
||||
|
||||
/*
|
||||
* Because 'struct key' is the first element in 'struct conf_entry',
|
||||
* it's safe (guaranteed by the C standard) to cast arbitrarily back
|
||||
* and forth between the two types. Therefore, we only need one
|
||||
* comparison function, which can double as a main sort function for
|
||||
* the tree (comparing two conf_entry structures with each other)
|
||||
* and a search function (looking up an externally supplied key).
|
||||
*/
|
||||
static int conf_cmp(void *av, void *bv)
|
||||
{
|
||||
struct key *a = (struct key *)av;
|
||||
struct key *b = (struct key *)bv;
|
||||
|
||||
if (a->primary < b->primary)
|
||||
return -1;
|
||||
else if (a->primary > b->primary)
|
||||
return +1;
|
||||
switch (subkeytypes[a->primary]) {
|
||||
case TYPE_INT:
|
||||
if (a->secondary.i < b->secondary.i)
|
||||
return -1;
|
||||
else if (a->secondary.i > b->secondary.i)
|
||||
return +1;
|
||||
return 0;
|
||||
case TYPE_STR:
|
||||
return strcmp(a->secondary.s, b->secondary.s);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int conf_cmp_constkey(void *av, void *bv)
|
||||
{
|
||||
struct key *a = (struct key *)av;
|
||||
struct constkey *b = (struct constkey *)bv;
|
||||
|
||||
if (a->primary < b->primary)
|
||||
return -1;
|
||||
else if (a->primary > b->primary)
|
||||
return +1;
|
||||
switch (subkeytypes[a->primary]) {
|
||||
case TYPE_INT:
|
||||
if (a->secondary.i < b->secondary.i)
|
||||
return -1;
|
||||
else if (a->secondary.i > b->secondary.i)
|
||||
return +1;
|
||||
return 0;
|
||||
case TYPE_STR:
|
||||
return strcmp(a->secondary.s, b->secondary.s);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Free any dynamic data items pointed to by a 'struct key'. We
|
||||
* don't free the structure itself, since it's probably part of a
|
||||
* larger allocated block.
|
||||
*/
|
||||
static void free_key(struct key *key)
|
||||
{
|
||||
if (subkeytypes[key->primary] == TYPE_STR)
|
||||
sfree(key->secondary.s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy a 'struct key' into another one, copying its dynamic data
|
||||
* if necessary.
|
||||
*/
|
||||
static void copy_key(struct key *to, struct key *from)
|
||||
{
|
||||
to->primary = from->primary;
|
||||
switch (subkeytypes[to->primary]) {
|
||||
case TYPE_INT:
|
||||
to->secondary.i = from->secondary.i;
|
||||
break;
|
||||
case TYPE_STR:
|
||||
to->secondary.s = dupstr(from->secondary.s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Free any dynamic data items pointed to by a 'struct value'. We
|
||||
* don't free the value itself, since it's probably part of a larger
|
||||
* allocated block.
|
||||
*/
|
||||
static void free_value(struct value *val, int type)
|
||||
{
|
||||
if (type == TYPE_STR)
|
||||
sfree(val->u.stringval);
|
||||
else if (type == TYPE_FILENAME)
|
||||
filename_free(val->u.fileval);
|
||||
else if (type == TYPE_FONT)
|
||||
fontspec_free(val->u.fontval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy a 'struct value' into another one, copying its dynamic data
|
||||
* if necessary.
|
||||
*/
|
||||
static void copy_value(struct value *to, struct value *from, int type)
|
||||
{
|
||||
switch (type) {
|
||||
case TYPE_BOOL:
|
||||
to->u.boolval = from->u.boolval;
|
||||
break;
|
||||
case TYPE_INT:
|
||||
to->u.intval = from->u.intval;
|
||||
break;
|
||||
case TYPE_STR:
|
||||
to->u.stringval = dupstr(from->u.stringval);
|
||||
break;
|
||||
case TYPE_FILENAME:
|
||||
to->u.fileval = filename_copy(from->u.fileval);
|
||||
break;
|
||||
case TYPE_FONT:
|
||||
to->u.fontval = fontspec_copy(from->u.fontval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Free an entire 'struct conf_entry' and its dynamic data.
|
||||
*/
|
||||
static void free_entry(struct conf_entry *entry)
|
||||
{
|
||||
free_key(&entry->key);
|
||||
free_value(&entry->value, valuetypes[entry->key.primary]);
|
||||
sfree(entry);
|
||||
}
|
||||
|
||||
Conf *conf_new(void)
|
||||
{
|
||||
Conf *conf = snew(struct conf_tag);
|
||||
|
||||
conf->tree = newtree234(conf_cmp);
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
static void conf_clear(Conf *conf)
|
||||
{
|
||||
struct conf_entry *entry;
|
||||
|
||||
while ((entry = delpos234(conf->tree, 0)) != NULL)
|
||||
free_entry(entry);
|
||||
}
|
||||
|
||||
void conf_free(Conf *conf)
|
||||
{
|
||||
conf_clear(conf);
|
||||
freetree234(conf->tree);
|
||||
sfree(conf);
|
||||
}
|
||||
|
||||
static void conf_insert(Conf *conf, struct conf_entry *entry)
|
||||
{
|
||||
struct conf_entry *oldentry = add234(conf->tree, entry);
|
||||
if (oldentry && oldentry != entry) {
|
||||
del234(conf->tree, oldentry);
|
||||
free_entry(oldentry);
|
||||
oldentry = add234(conf->tree, entry);
|
||||
assert(oldentry == entry);
|
||||
}
|
||||
}
|
||||
|
||||
void conf_copy_into(Conf *newconf, Conf *oldconf)
|
||||
{
|
||||
struct conf_entry *entry, *entry2;
|
||||
int i;
|
||||
|
||||
conf_clear(newconf);
|
||||
|
||||
for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
|
||||
entry2 = snew(struct conf_entry);
|
||||
copy_key(&entry2->key, &entry->key);
|
||||
copy_value(&entry2->value, &entry->value,
|
||||
valuetypes[entry->key.primary]);
|
||||
add234(newconf->tree, entry2);
|
||||
}
|
||||
}
|
||||
|
||||
Conf *conf_copy(Conf *oldconf)
|
||||
{
|
||||
Conf *newconf = conf_new();
|
||||
|
||||
conf_copy_into(newconf, oldconf);
|
||||
|
||||
return newconf;
|
||||
}
|
||||
|
||||
bool conf_get_bool(Conf *conf, int primary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
#ifdef MOD_PERSO
|
||||
if( valuetypes[primary] == TYPE_INT ) {
|
||||
int i = conf_get_int( conf, primary ) ;
|
||||
if( i==0 ) { return false ; }
|
||||
return true ;
|
||||
}
|
||||
#endif
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_BOOL);
|
||||
key.primary = primary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.boolval;
|
||||
}
|
||||
|
||||
int conf_get_int(Conf *conf, int primary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
#ifdef MOD_PERSO
|
||||
if( valuetypes[primary] == TYPE_BOOL ) {
|
||||
return (conf_get_bool( conf, primary ) ? 1 : 0) ;
|
||||
}
|
||||
#endif
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_INT);
|
||||
key.primary = primary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.intval;
|
||||
}
|
||||
|
||||
int conf_get_int_int(Conf *conf, int primary, int secondary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_INT);
|
||||
assert(valuetypes[primary] == TYPE_INT);
|
||||
key.primary = primary;
|
||||
key.secondary.i = secondary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.intval;
|
||||
}
|
||||
|
||||
char *conf_get_str(Conf *conf, int primary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
key.primary = primary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.stringval;
|
||||
}
|
||||
|
||||
char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_STR);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
key.primary = primary;
|
||||
key.secondary.s = (char *)secondary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
return entry ? entry->value.u.stringval : NULL;
|
||||
}
|
||||
|
||||
char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
|
||||
{
|
||||
char *ret = conf_get_str_str_opt(conf, primary, secondary);
|
||||
assert(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *conf_get_str_strs(Conf *conf, int primary,
|
||||
char *subkeyin, char **subkeyout)
|
||||
{
|
||||
struct constkey key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_STR);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
key.primary = primary;
|
||||
if (subkeyin) {
|
||||
key.secondary.s = subkeyin;
|
||||
entry = findrel234(conf->tree, &key, NULL, REL234_GT);
|
||||
} else {
|
||||
key.secondary.s = "";
|
||||
entry = findrel234(conf->tree, &key, conf_cmp_constkey, REL234_GE);
|
||||
}
|
||||
if (!entry || entry->key.primary != primary)
|
||||
return NULL;
|
||||
*subkeyout = entry->key.secondary.s;
|
||||
return entry->value.u.stringval;
|
||||
}
|
||||
|
||||
char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
|
||||
{
|
||||
struct constkey key;
|
||||
struct conf_entry *entry;
|
||||
int index;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_STR);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
key.primary = primary;
|
||||
key.secondary.s = "";
|
||||
entry = findrelpos234(conf->tree, &key, conf_cmp_constkey,
|
||||
REL234_GE, &index);
|
||||
if (!entry || entry->key.primary != primary)
|
||||
return NULL;
|
||||
entry = index234(conf->tree, index + n);
|
||||
if (!entry || entry->key.primary != primary)
|
||||
return NULL;
|
||||
return entry->key.secondary.s;
|
||||
}
|
||||
|
||||
Filename *conf_get_filename(Conf *conf, int primary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_FILENAME);
|
||||
key.primary = primary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.fileval;
|
||||
}
|
||||
|
||||
FontSpec *conf_get_fontspec(Conf *conf, int primary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_FONT);
|
||||
key.primary = primary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
assert(entry);
|
||||
return entry->value.u.fontval;
|
||||
}
|
||||
|
||||
void conf_set_bool(Conf *conf, int primary, bool value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
#ifdef MOD_PERSO
|
||||
if( valuetypes[primary] == TYPE_INT ) {
|
||||
conf_set_int( conf, primary, (value?1:0) );
|
||||
return ;
|
||||
}
|
||||
#endif
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_BOOL);
|
||||
entry->key.primary = primary;
|
||||
entry->value.u.boolval = value;
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_set_int(Conf *conf, int primary, int value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
#ifdef MOD_PERSO
|
||||
if( valuetypes[primary] == TYPE_BOOL ) {
|
||||
conf_set_bool( conf, primary, (value==0?false:true) );
|
||||
return ;
|
||||
}
|
||||
#endif
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_INT);
|
||||
entry->key.primary = primary;
|
||||
entry->value.u.intval = value;
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_set_int_int(Conf *conf, int primary,
|
||||
int secondary, int value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_INT);
|
||||
assert(valuetypes[primary] == TYPE_INT);
|
||||
entry->key.primary = primary;
|
||||
entry->key.secondary.i = secondary;
|
||||
entry->value.u.intval = value;
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_set_str(Conf *conf, int primary, const char *value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
entry->key.primary = primary;
|
||||
entry->value.u.stringval = dupstr(value);
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_set_str_str(Conf *conf, int primary, const char *secondary,
|
||||
const char *value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_STR);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
entry->key.primary = primary;
|
||||
entry->key.secondary.s = dupstr(secondary);
|
||||
entry->value.u.stringval = dupstr(value);
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_del_str_str(Conf *conf, int primary, const char *secondary)
|
||||
{
|
||||
struct key key;
|
||||
struct conf_entry *entry;
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_STR);
|
||||
assert(valuetypes[primary] == TYPE_STR);
|
||||
key.primary = primary;
|
||||
key.secondary.s = (char *)secondary;
|
||||
entry = find234(conf->tree, &key, NULL);
|
||||
if (entry) {
|
||||
del234(conf->tree, entry);
|
||||
free_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void conf_set_filename(Conf *conf, int primary, const Filename *value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_FILENAME);
|
||||
entry->key.primary = primary;
|
||||
entry->value.u.fileval = filename_copy(value);
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
|
||||
{
|
||||
struct conf_entry *entry = snew(struct conf_entry);
|
||||
|
||||
assert(subkeytypes[primary] == TYPE_NONE);
|
||||
assert(valuetypes[primary] == TYPE_FONT);
|
||||
entry->key.primary = primary;
|
||||
entry->value.u.fontval = fontspec_copy(value);
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
|
||||
void conf_serialise(BinarySink *bs, Conf *conf)
|
||||
{
|
||||
int i;
|
||||
struct conf_entry *entry;
|
||||
|
||||
for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
|
||||
put_uint32(bs, entry->key.primary);
|
||||
|
||||
switch (subkeytypes[entry->key.primary]) {
|
||||
case TYPE_INT:
|
||||
put_uint32(bs, entry->key.secondary.i);
|
||||
break;
|
||||
case TYPE_STR:
|
||||
put_asciz(bs, entry->key.secondary.s);
|
||||
break;
|
||||
}
|
||||
switch (valuetypes[entry->key.primary]) {
|
||||
case TYPE_BOOL:
|
||||
put_bool(bs, entry->value.u.boolval);
|
||||
break;
|
||||
case TYPE_INT:
|
||||
put_uint32(bs, entry->value.u.intval);
|
||||
break;
|
||||
case TYPE_STR:
|
||||
put_asciz(bs, entry->value.u.stringval);
|
||||
break;
|
||||
case TYPE_FILENAME:
|
||||
filename_serialise(bs, entry->value.u.fileval);
|
||||
break;
|
||||
case TYPE_FONT:
|
||||
fontspec_serialise(bs, entry->value.u.fontval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
put_uint32(bs, 0xFFFFFFFFU);
|
||||
}
|
||||
|
||||
bool conf_deserialise(Conf *conf, BinarySource *src)
|
||||
{
|
||||
struct conf_entry *entry;
|
||||
unsigned primary;
|
||||
|
||||
while (1) {
|
||||
primary = get_uint32(src);
|
||||
|
||||
if (get_err(src))
|
||||
return false;
|
||||
if (primary == 0xFFFFFFFFU)
|
||||
return true;
|
||||
if (primary >= N_CONFIG_OPTIONS)
|
||||
return false;
|
||||
|
||||
entry = snew(struct conf_entry);
|
||||
entry->key.primary = primary;
|
||||
|
||||
switch (subkeytypes[entry->key.primary]) {
|
||||
case TYPE_INT:
|
||||
entry->key.secondary.i = toint(get_uint32(src));
|
||||
break;
|
||||
case TYPE_STR:
|
||||
entry->key.secondary.s = dupstr(get_asciz(src));
|
||||
break;
|
||||
}
|
||||
|
||||
switch (valuetypes[entry->key.primary]) {
|
||||
case TYPE_BOOL:
|
||||
entry->value.u.boolval = get_bool(src);
|
||||
break;
|
||||
case TYPE_INT:
|
||||
entry->value.u.intval = toint(get_uint32(src));
|
||||
break;
|
||||
case TYPE_STR:
|
||||
entry->value.u.stringval = dupstr(get_asciz(src));
|
||||
break;
|
||||
case TYPE_FILENAME:
|
||||
entry->value.u.fileval = filename_deserialise(src);
|
||||
break;
|
||||
case TYPE_FONT:
|
||||
entry->value.u.fontval = fontspec_deserialise(src);
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_err(src)) {
|
||||
free_entry(entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
conf_insert(conf, entry);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Common pieces between the platform console frontend modules.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "misc.h"
|
||||
#include "console.h"
|
||||
|
||||
const char hk_absentmsg_common_fmt[] =
|
||||
"The server's host key is not cached. You have no guarantee\n"
|
||||
"that the server is the computer you think it is.\n"
|
||||
"The server's %s key fingerprint is:\n"
|
||||
"%s\n";
|
||||
const char hk_absentmsg_interactive_intro[] =
|
||||
"If you trust this host, enter \"y\" to add the key to\n"
|
||||
"PuTTY's cache and carry on connecting.\n"
|
||||
"If you want to carry on connecting just once, without\n"
|
||||
"adding the key to the cache, enter \"n\".\n"
|
||||
"If you do not trust this host, press Return to abandon the\n"
|
||||
"connection.\n";
|
||||
const char hk_absentmsg_interactive_prompt[] =
|
||||
"Store key in cache? (y/n, Return cancels connection, "
|
||||
"i for more info) ";
|
||||
|
||||
const char hk_wrongmsg_common_fmt[] =
|
||||
"WARNING - POTENTIAL SECURITY BREACH!\n"
|
||||
"The server's host key does not match the one PuTTY has\n"
|
||||
"cached. This means that either the server administrator\n"
|
||||
"has changed the host key, or you have actually connected\n"
|
||||
"to another computer pretending to be the server.\n"
|
||||
"The new %s key fingerprint is:\n"
|
||||
"%s\n";
|
||||
const char hk_wrongmsg_interactive_intro[] =
|
||||
"If you were expecting this change and trust the new key,\n"
|
||||
"enter \"y\" to update PuTTY's cache and continue connecting.\n"
|
||||
"If you want to carry on connecting but without updating\n"
|
||||
"the cache, enter \"n\".\n"
|
||||
"If you want to abandon the connection completely, press\n"
|
||||
"Return to cancel. Pressing Return is the ONLY guaranteed\n"
|
||||
"safe choice.\n";
|
||||
const char hk_wrongmsg_interactive_prompt[] =
|
||||
"Update cached key? (y/n, Return cancels connection, "
|
||||
"i for more info) ";
|
||||
|
||||
const char weakcrypto_msg_common_fmt[] =
|
||||
"The first %s supported by the server is\n"
|
||||
"%s, which is below the configured warning threshold.\n";
|
||||
|
||||
const char weakhk_msg_common_fmt[] =
|
||||
"The first host key type we have stored for this server\n"
|
||||
"is %s, which is below the configured warning threshold.\n"
|
||||
"The server also provides the following types of host key\n"
|
||||
"above the threshold, which we do not have stored:\n"
|
||||
"%s\n";
|
||||
|
||||
const char console_continue_prompt[] = "Continue with connection? (y/n) ";
|
||||
const char console_abandoned_msg[] = "Connection abandoned.\n";
|
||||
|
||||
bool console_batch_mode = false;
|
||||
|
||||
/*
|
||||
* Error message and/or fatal exit functions, all based on
|
||||
* console_print_error_msg which the platform front end provides.
|
||||
*/
|
||||
void console_print_error_msg_fmt_v(
|
||||
const char *prefix, const char *fmt, va_list ap)
|
||||
{
|
||||
char *msg = dupvprintf(fmt, ap);
|
||||
console_print_error_msg(prefix, msg);
|
||||
sfree(msg);
|
||||
}
|
||||
|
||||
void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
console_print_error_msg_fmt_v(prefix, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void modalfatalbox(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
console_print_error_msg_fmt_v("FATAL ERROR", fmt, ap);
|
||||
va_end(ap);
|
||||
cleanup_exit(1);
|
||||
}
|
||||
|
||||
void nonfatal(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
console_print_error_msg_fmt_v("ERROR", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void console_connection_fatal(Seat *seat, const char *msg)
|
||||
{
|
||||
console_print_error_msg("FATAL ERROR", msg);
|
||||
cleanup_exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Console front ends redo their select() or equivalent every time, so
|
||||
* they don't need separate timer handling.
|
||||
*/
|
||||
void timer_change_notify(unsigned long next)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Common pieces between the platform console frontend modules.
|
||||
*/
|
||||
|
||||
extern const char hk_absentmsg_common_fmt[];
|
||||
extern const char hk_absentmsg_interactive_intro[];
|
||||
extern const char hk_absentmsg_interactive_prompt[];
|
||||
extern const char hk_wrongmsg_common_fmt[];
|
||||
extern const char hk_wrongmsg_interactive_intro[];
|
||||
extern const char hk_wrongmsg_interactive_prompt[];
|
||||
|
||||
extern const char weakcrypto_msg_common_fmt[];
|
||||
|
||||
extern const char weakhk_msg_common_fmt[];
|
||||
|
||||
extern const char console_continue_prompt[];
|
||||
extern const char console_abandoned_msg[];
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Routines to do cryptographic interaction with proxies in PuTTY.
|
||||
* This is in a separate module from proxy.c, so that it can be
|
||||
* conveniently removed in PuTTYtel by replacing this module with
|
||||
* the stub version nocproxy.c.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h" /* For MD5 support */
|
||||
#include "network.h"
|
||||
#include "proxy.h"
|
||||
#include "marshal.h"
|
||||
|
||||
static void hmacmd5_chap(const unsigned char *challenge, int challen,
|
||||
const char *passwd, unsigned char *response)
|
||||
{
|
||||
mac_simple(&ssh_hmac_md5, ptrlen_from_asciz(passwd),
|
||||
make_ptrlen(challenge, challen), response);
|
||||
}
|
||||
|
||||
void proxy_socks5_offerencryptedauth(BinarySink *bs)
|
||||
{
|
||||
put_byte(bs, 0x03); /* CHAP */
|
||||
}
|
||||
|
||||
int proxy_socks5_handlechap (ProxySocket *p)
|
||||
{
|
||||
|
||||
/* CHAP authentication reply format:
|
||||
* version number (1 bytes) = 1
|
||||
* number of commands (1 byte)
|
||||
*
|
||||
* For each command:
|
||||
* command identifier (1 byte)
|
||||
* data length (1 byte)
|
||||
*/
|
||||
unsigned char data[260];
|
||||
unsigned char outbuf[20];
|
||||
|
||||
while(p->chap_num_attributes == 0 ||
|
||||
p->chap_num_attributes_processed < p->chap_num_attributes) {
|
||||
if (p->chap_num_attributes == 0 ||
|
||||
p->chap_current_attribute == -1) {
|
||||
/* CHAP normally reads in two bytes, either at the
|
||||
* beginning or for each attribute/value pair. But if
|
||||
* we're waiting for the value's data, we might not want
|
||||
* to read 2 bytes.
|
||||
*/
|
||||
|
||||
if (bufchain_size(&p->pending_input_data) < 2)
|
||||
return 1; /* not got anything yet */
|
||||
|
||||
/* get the response */
|
||||
bufchain_fetch(&p->pending_input_data, data, 2);
|
||||
bufchain_consume(&p->pending_input_data, 2);
|
||||
}
|
||||
|
||||
if (p->chap_num_attributes == 0) {
|
||||
/* If there are no attributes, this is our first msg
|
||||
* with the server, where we negotiate version and
|
||||
* number of attributes
|
||||
*/
|
||||
if (data[0] != 0x01) {
|
||||
plug_closing(p->plug, "Proxy error: SOCKS proxy wants"
|
||||
" a different CHAP version",
|
||||
PROXY_ERROR_GENERAL, 0);
|
||||
return 1;
|
||||
}
|
||||
if (data[1] == 0x00) {
|
||||
plug_closing(p->plug, "Proxy error: SOCKS proxy won't"
|
||||
" negotiate CHAP with us",
|
||||
PROXY_ERROR_GENERAL, 0);
|
||||
return 1;
|
||||
}
|
||||
p->chap_num_attributes = data[1];
|
||||
} else {
|
||||
if (p->chap_current_attribute == -1) {
|
||||
/* We have to read in each attribute/value pair -
|
||||
* those we don't understand can be ignored, but
|
||||
* there are a few we'll need to handle.
|
||||
*/
|
||||
p->chap_current_attribute = data[0];
|
||||
p->chap_current_datalen = data[1];
|
||||
}
|
||||
if (bufchain_size(&p->pending_input_data) <
|
||||
p->chap_current_datalen)
|
||||
return 1; /* not got everything yet */
|
||||
|
||||
/* get the response */
|
||||
bufchain_fetch(&p->pending_input_data, data,
|
||||
p->chap_current_datalen);
|
||||
|
||||
bufchain_consume(&p->pending_input_data,
|
||||
p->chap_current_datalen);
|
||||
|
||||
switch (p->chap_current_attribute) {
|
||||
case 0x00:
|
||||
/* Successful authentication */
|
||||
if (data[0] == 0x00)
|
||||
p->state = 2;
|
||||
else {
|
||||
plug_closing(p->plug, "Proxy error: SOCKS proxy"
|
||||
" refused CHAP authentication",
|
||||
PROXY_ERROR_GENERAL, 0);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
outbuf[0] = 0x01; /* Version */
|
||||
outbuf[1] = 0x01; /* One attribute */
|
||||
outbuf[2] = 0x04; /* Response */
|
||||
outbuf[3] = 0x10; /* Length */
|
||||
hmacmd5_chap(data, p->chap_current_datalen,
|
||||
conf_get_str(p->conf, CONF_proxy_password),
|
||||
&outbuf[4]);
|
||||
sk_write(p->sub_socket, outbuf, 20);
|
||||
break;
|
||||
case 0x11:
|
||||
/* Chose a protocol */
|
||||
if (data[0] != 0x85) {
|
||||
plug_closing(p->plug, "Proxy error: Server chose "
|
||||
"CHAP of other than HMAC-MD5 but we "
|
||||
"didn't offer it!",
|
||||
PROXY_ERROR_GENERAL, 0);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
p->chap_current_attribute = -1;
|
||||
p->chap_num_attributes_processed++;
|
||||
}
|
||||
if (p->state == 8 &&
|
||||
p->chap_num_attributes_processed >= p->chap_num_attributes) {
|
||||
p->chap_num_attributes = 0;
|
||||
p->chap_num_attributes_processed = 0;
|
||||
p->chap_current_datalen = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proxy_socks5_selectchap(ProxySocket *p)
|
||||
{
|
||||
char *username = conf_get_str(p->conf, CONF_proxy_username);
|
||||
char *password = conf_get_str(p->conf, CONF_proxy_password);
|
||||
if (username[0] || password[0]) {
|
||||
char chapbuf[514];
|
||||
int ulen;
|
||||
chapbuf[0] = '\x01'; /* Version */
|
||||
chapbuf[1] = '\x02'; /* Number of attributes sent */
|
||||
chapbuf[2] = '\x11'; /* First attribute - algorithms list */
|
||||
chapbuf[3] = '\x01'; /* Only one CHAP algorithm */
|
||||
chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */
|
||||
chapbuf[5] = '\x02'; /* Second attribute - username */
|
||||
|
||||
ulen = strlen(username);
|
||||
if (ulen > 255) ulen = 255;
|
||||
if (ulen < 1) ulen = 1;
|
||||
|
||||
chapbuf[6] = ulen;
|
||||
memcpy(chapbuf+7, username, ulen);
|
||||
|
||||
sk_write(p->sub_socket, chapbuf, ulen + 7);
|
||||
p->chap_num_attributes = 0;
|
||||
p->chap_num_attributes_processed = 0;
|
||||
p->chap_current_attribute = -1;
|
||||
p->chap_current_datalen = 0;
|
||||
|
||||
p->state = 8;
|
||||
} else
|
||||
plug_closing(p->plug, "Proxy error: Server chose "
|
||||
"CHAP authentication but we didn't offer it!",
|
||||
PROXY_ERROR_GENERAL, 0);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* defs.h: initial definitions for PuTTY.
|
||||
*
|
||||
* The rule about this header file is that it can't depend on any
|
||||
* other header file in this code base. This is where we define
|
||||
* things, as much as we can, that other headers will want to refer
|
||||
* to, such as opaque structure types and their associated typedefs,
|
||||
* or macros that are used by other headers.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_DEFS_H
|
||||
#define PUTTY_DEFS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h> /* for __MINGW_PRINTF_FORMAT */
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined _MSC_VER && _MSC_VER < 1800
|
||||
/* Work around lack of inttypes.h and strtoumax in older MSVC */
|
||||
#define PRIx32 "x"
|
||||
#define PRIu32 "u"
|
||||
#define PRIu64 "I64u"
|
||||
#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;
|
||||
|
||||
typedef struct Filename Filename;
|
||||
typedef struct FontSpec FontSpec;
|
||||
|
||||
typedef struct bufchain_tag bufchain;
|
||||
|
||||
typedef struct strbuf strbuf;
|
||||
typedef struct LoadedFile LoadedFile;
|
||||
|
||||
typedef struct RSAKey RSAKey;
|
||||
|
||||
typedef struct BinarySink BinarySink;
|
||||
typedef struct BinarySource BinarySource;
|
||||
typedef struct stdio_sink stdio_sink;
|
||||
typedef struct bufchain_sink bufchain_sink;
|
||||
typedef struct handle_sink handle_sink;
|
||||
|
||||
typedef struct IdempotentCallback IdempotentCallback;
|
||||
|
||||
typedef struct SockAddr SockAddr;
|
||||
|
||||
typedef struct Socket Socket;
|
||||
typedef struct Plug Plug;
|
||||
typedef struct SocketPeerInfo SocketPeerInfo;
|
||||
|
||||
typedef struct Backend Backend;
|
||||
typedef struct BackendVtable BackendVtable;
|
||||
|
||||
typedef struct Ldisc_tag Ldisc;
|
||||
typedef struct LogContext LogContext;
|
||||
typedef struct LogPolicy LogPolicy;
|
||||
typedef struct LogPolicyVtable LogPolicyVtable;
|
||||
|
||||
typedef struct Seat Seat;
|
||||
typedef struct SeatVtable SeatVtable;
|
||||
|
||||
typedef struct TermWin TermWin;
|
||||
typedef struct TermWinVtable TermWinVtable;
|
||||
|
||||
typedef struct Ssh Ssh;
|
||||
|
||||
typedef struct mp_int mp_int;
|
||||
typedef struct MontyContext MontyContext;
|
||||
|
||||
typedef struct WeierstrassCurve WeierstrassCurve;
|
||||
typedef struct WeierstrassPoint WeierstrassPoint;
|
||||
typedef struct MontgomeryCurve MontgomeryCurve;
|
||||
typedef struct MontgomeryPoint MontgomeryPoint;
|
||||
typedef struct EdwardsCurve EdwardsCurve;
|
||||
typedef struct EdwardsPoint EdwardsPoint;
|
||||
|
||||
typedef struct SshServerConfig SshServerConfig;
|
||||
typedef struct SftpServer SftpServer;
|
||||
typedef struct SftpServerVtable SftpServerVtable;
|
||||
|
||||
typedef struct Channel Channel;
|
||||
typedef struct SshChannel SshChannel;
|
||||
typedef struct mainchan mainchan;
|
||||
|
||||
typedef struct ssh_sharing_state ssh_sharing_state;
|
||||
typedef struct ssh_sharing_connstate ssh_sharing_connstate;
|
||||
typedef struct share_channel share_channel;
|
||||
|
||||
typedef struct PortFwdManager PortFwdManager;
|
||||
typedef struct PortFwdRecord PortFwdRecord;
|
||||
typedef struct ConnectionLayer ConnectionLayer;
|
||||
|
||||
typedef struct prng prng;
|
||||
typedef struct ssh_hashalg ssh_hashalg;
|
||||
typedef struct ssh_hash ssh_hash;
|
||||
typedef struct ssh_kex ssh_kex;
|
||||
typedef struct ssh_kexes ssh_kexes;
|
||||
typedef struct ssh_keyalg ssh_keyalg;
|
||||
typedef struct ssh_key ssh_key;
|
||||
typedef struct ssh_compressor ssh_compressor;
|
||||
typedef struct ssh_decompressor ssh_decompressor;
|
||||
typedef struct ssh_compression_alg ssh_compression_alg;
|
||||
typedef struct ssh2_userkey ssh2_userkey;
|
||||
typedef struct ssh2_macalg ssh2_macalg;
|
||||
typedef struct ssh2_mac ssh2_mac;
|
||||
typedef struct ssh_cipheralg ssh_cipheralg;
|
||||
typedef struct ssh_cipher ssh_cipher;
|
||||
typedef struct ssh2_ciphers ssh2_ciphers;
|
||||
typedef struct dh_ctx dh_ctx;
|
||||
typedef struct ecdh_key ecdh_key;
|
||||
|
||||
typedef struct dlgparam dlgparam;
|
||||
|
||||
typedef struct settings_w settings_w;
|
||||
typedef struct settings_r settings_r;
|
||||
typedef struct settings_e settings_e;
|
||||
|
||||
typedef struct SessionSpecial SessionSpecial;
|
||||
|
||||
typedef struct StripCtrlChars StripCtrlChars;
|
||||
|
||||
/*
|
||||
* A small structure wrapping up a (pointer, length) pair so that it
|
||||
* can be conveniently passed to or from a function.
|
||||
*/
|
||||
typedef struct ptrlen {
|
||||
const void *ptr;
|
||||
size_t len;
|
||||
} ptrlen;
|
||||
|
||||
typedef struct logblank_t logblank_t;
|
||||
|
||||
typedef struct BinaryPacketProtocol BinaryPacketProtocol;
|
||||
typedef struct PacketProtocolLayer PacketProtocolLayer;
|
||||
|
||||
/* Do a compile-time type-check of 'to_check' (without evaluating it),
|
||||
* as a side effect of returning the value 'to_return'. Note that
|
||||
* although this macro double-*expands* to_return, it always
|
||||
* *evaluates* exactly one copy of it, so it's side-effect safe. */
|
||||
#define TYPECHECK(to_check, to_return) \
|
||||
(sizeof(to_check) ? (to_return) : (to_return))
|
||||
|
||||
/* Return a pointer to the object of structure type 'type' whose field
|
||||
* with name 'field' is pointed at by 'object'. */
|
||||
#define container_of(object, type, field) \
|
||||
TYPECHECK(object == &((type *)0)->field, \
|
||||
((type *)(((char *)(object)) - offsetof(type, field))))
|
||||
|
||||
#if defined __GNUC__ || defined __clang__
|
||||
#define NORETURN __attribute__((__noreturn__))
|
||||
#elif defined _MSC_VER
|
||||
#define NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define NORETURN
|
||||
#endif
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Platform-specific definitions.
|
||||
*
|
||||
* Most of these live in the per-platform header files, of which
|
||||
* puttyps.h selects the appropriate one. But some of the sources
|
||||
* (particularly standalone test applications) would prefer not to
|
||||
* have to include a per-platform header at all, because that makes it
|
||||
* more portable to platforms not supported by the code base as a
|
||||
* whole (for example, compiling purely computational parts of the
|
||||
* code for specialist platforms for test and analysis purposes). So
|
||||
* any definition that has to affect even _those_ modules will have to
|
||||
* go here, with the key constraint being that this code has to come
|
||||
* to _some_ decision even if the compilation platform is not a
|
||||
* recognised one at all.
|
||||
*/
|
||||
|
||||
/* Purely computational code uses smemclr(), so we have to make the
|
||||
* decision here about whether that's provided by utils.c or by a
|
||||
* platform implementation. We define PLATFORM_HAS_SMEMCLR to suppress
|
||||
* utils.c's definition. */
|
||||
#ifdef _WINDOWS
|
||||
/* Windows provides the API function 'SecureZeroMemory', which we use
|
||||
* unless the user has told us not to by defining NO_SECUREZEROMEMORY. */
|
||||
#ifndef NO_SECUREZEROMEMORY
|
||||
#define PLATFORM_HAS_SMEMCLR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* PUTTY_DEFS_H */
|
||||
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* dialog.c - a reasonably platform-independent mechanism for
|
||||
* describing dialog boxes.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DEFINE_INTORPTR_FNS
|
||||
|
||||
#include "putty.h"
|
||||
#include "dialog.h"
|
||||
|
||||
int ctrl_path_elements(const char *path)
|
||||
{
|
||||
int i = 1;
|
||||
while (*path) {
|
||||
if (*path == '/') i++;
|
||||
path++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Return the number of matching path elements at the starts of p1 and p2,
|
||||
* or INT_MAX if the paths are identical. */
|
||||
int ctrl_path_compare(const char *p1, const char *p2)
|
||||
{
|
||||
int i = 0;
|
||||
while (*p1 || *p2) {
|
||||
if ((*p1 == '/' || *p1 == '\0') &&
|
||||
(*p2 == '/' || *p2 == '\0'))
|
||||
i++; /* a whole element matches, ooh */
|
||||
if (*p1 != *p2)
|
||||
return i; /* mismatch */
|
||||
p1++, p2++;
|
||||
}
|
||||
return INT_MAX; /* exact match */
|
||||
}
|
||||
|
||||
struct controlbox *ctrl_new_box(void)
|
||||
{
|
||||
struct controlbox *ret = snew(struct controlbox);
|
||||
|
||||
ret->nctrlsets = ret->ctrlsetsize = 0;
|
||||
ret->ctrlsets = NULL;
|
||||
ret->nfrees = ret->freesize = 0;
|
||||
ret->frees = NULL;
|
||||
ret->freefuncs = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ctrl_free_box(struct controlbox *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < b->nctrlsets; i++) {
|
||||
ctrl_free_set(b->ctrlsets[i]);
|
||||
}
|
||||
for (i = 0; i < b->nfrees; i++)
|
||||
b->freefuncs[i](b->frees[i]);
|
||||
sfree(b->ctrlsets);
|
||||
sfree(b->frees);
|
||||
sfree(b->freefuncs);
|
||||
sfree(b);
|
||||
}
|
||||
|
||||
void ctrl_free_set(struct controlset *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
sfree(s->pathname);
|
||||
sfree(s->boxname);
|
||||
sfree(s->boxtitle);
|
||||
for (i = 0; i < s->ncontrols; i++) {
|
||||
ctrl_free(s->ctrls[i]);
|
||||
}
|
||||
sfree(s->ctrls);
|
||||
sfree(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the index of first controlset in a controlbox for a given
|
||||
* path. If that path doesn't exist, return the index where it
|
||||
* should be inserted.
|
||||
*/
|
||||
static int ctrl_find_set(struct controlbox *b, const char *path, bool start)
|
||||
{
|
||||
int i, last, thisone;
|
||||
|
||||
last = 0;
|
||||
for (i = 0; i < b->nctrlsets; i++) {
|
||||
thisone = ctrl_path_compare(path, b->ctrlsets[i]->pathname);
|
||||
/*
|
||||
* If `start' is true and there exists a controlset with
|
||||
* exactly the path we've been given, we should return the
|
||||
* index of the first such controlset we find. Otherwise,
|
||||
* we should return the index of the first entry in which
|
||||
* _fewer_ path elements match than they did last time.
|
||||
*/
|
||||
if ((start && thisone == INT_MAX) || thisone < last)
|
||||
return i;
|
||||
last = thisone;
|
||||
}
|
||||
return b->nctrlsets; /* insert at end */
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the index of next controlset in a controlbox for a given
|
||||
* path, or -1 if no such controlset exists. If -1 is passed as
|
||||
* input, finds the first.
|
||||
*/
|
||||
int ctrl_find_path(struct controlbox *b, const char *path, int index)
|
||||
{
|
||||
if (index < 0)
|
||||
index = ctrl_find_set(b, path, true);
|
||||
else
|
||||
index++;
|
||||
|
||||
if (index < b->nctrlsets && !strcmp(path, b->ctrlsets[index]->pathname))
|
||||
return index;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set up a panel title. */
|
||||
struct controlset *ctrl_settitle(struct controlbox *b,
|
||||
const char *path, const char *title)
|
||||
{
|
||||
|
||||
struct controlset *s = snew(struct controlset);
|
||||
int index = ctrl_find_set(b, path, true);
|
||||
s->pathname = dupstr(path);
|
||||
s->boxname = NULL;
|
||||
s->boxtitle = dupstr(title);
|
||||
s->ncontrols = s->ctrlsize = 0;
|
||||
s->ncolumns = 0; /* this is a title! */
|
||||
s->ctrls = NULL;
|
||||
sgrowarray(b->ctrlsets, b->ctrlsetsize, b->nctrlsets);
|
||||
if (index < b->nctrlsets)
|
||||
memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],
|
||||
(b->nctrlsets-index) * sizeof(*b->ctrlsets));
|
||||
b->ctrlsets[index] = s;
|
||||
b->nctrlsets++;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Retrieve a pointer to a controlset, creating it if absent. */
|
||||
struct controlset *ctrl_getset(struct controlbox *b, const char *path,
|
||||
const char *name, const char *boxtitle)
|
||||
{
|
||||
struct controlset *s;
|
||||
int index = ctrl_find_set(b, path, true);
|
||||
while (index < b->nctrlsets &&
|
||||
!strcmp(b->ctrlsets[index]->pathname, path)) {
|
||||
if (b->ctrlsets[index]->boxname &&
|
||||
!strcmp(b->ctrlsets[index]->boxname, name))
|
||||
return b->ctrlsets[index];
|
||||
index++;
|
||||
}
|
||||
s = snew(struct controlset);
|
||||
s->pathname = dupstr(path);
|
||||
s->boxname = dupstr(name);
|
||||
s->boxtitle = boxtitle ? dupstr(boxtitle) : NULL;
|
||||
s->ncolumns = 1;
|
||||
s->ncontrols = s->ctrlsize = 0;
|
||||
s->ctrls = NULL;
|
||||
sgrowarray(b->ctrlsets, b->ctrlsetsize, b->nctrlsets);
|
||||
if (index < b->nctrlsets)
|
||||
memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],
|
||||
(b->nctrlsets-index) * sizeof(*b->ctrlsets));
|
||||
b->ctrlsets[index] = s;
|
||||
b->nctrlsets++;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Allocate some private data in a controlbox. */
|
||||
void *ctrl_alloc_with_free(struct controlbox *b, size_t size,
|
||||
ctrl_freefn_t freefunc)
|
||||
{
|
||||
void *p;
|
||||
/*
|
||||
* This is an internal allocation routine, so it's allowed to
|
||||
* use smalloc directly.
|
||||
*/
|
||||
p = smalloc(size);
|
||||
sgrowarray(b->frees, b->freesize, b->nfrees);
|
||||
b->freefuncs = sresize(b->freefuncs, b->freesize, ctrl_freefn_t);
|
||||
b->frees[b->nfrees] = p;
|
||||
b->freefuncs[b->nfrees] = freefunc;
|
||||
b->nfrees++;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void ctrl_default_free(void *p)
|
||||
{
|
||||
sfree(p);
|
||||
}
|
||||
|
||||
void *ctrl_alloc(struct controlbox *b, size_t size)
|
||||
{
|
||||
return ctrl_alloc_with_free(b, size, ctrl_default_free);
|
||||
}
|
||||
|
||||
static union control *ctrl_new(struct controlset *s, int type,
|
||||
intorptr helpctx, handler_fn handler,
|
||||
intorptr context)
|
||||
{
|
||||
union control *c = snew(union control);
|
||||
sgrowarray(s->ctrls, s->ctrlsize, s->ncontrols);
|
||||
s->ctrls[s->ncontrols++] = c;
|
||||
/*
|
||||
* Fill in the standard fields.
|
||||
*/
|
||||
c->generic.type = type;
|
||||
c->generic.tabdelay = false;
|
||||
c->generic.column = COLUMN_FIELD(0, s->ncolumns);
|
||||
c->generic.helpctx = helpctx;
|
||||
c->generic.handler = handler;
|
||||
c->generic.context = context;
|
||||
c->generic.label = NULL;
|
||||
c->generic.align_next_to = NULL;
|
||||
return c;
|
||||
}
|
||||
|
||||
/* `ncolumns' is followed by that many percentages, as integers. */
|
||||
union control *ctrl_columns(struct controlset *s, int ncolumns, ...)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_COLUMNS, P(NULL), NULL, P(NULL));
|
||||
assert(s->ncolumns == 1 || ncolumns == 1);
|
||||
c->columns.ncols = ncolumns;
|
||||
s->ncolumns = ncolumns;
|
||||
if (ncolumns == 1) {
|
||||
c->columns.percentages = NULL;
|
||||
} else {
|
||||
va_list ap;
|
||||
int i;
|
||||
c->columns.percentages = snewn(ncolumns, int);
|
||||
va_start(ap, ncolumns);
|
||||
for (i = 0; i < ncolumns; i++)
|
||||
c->columns.percentages[i] = va_arg(ap, int);
|
||||
va_end(ap);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_editbox(struct controlset *s, const char *label,
|
||||
char shortcut, int percentage,
|
||||
intorptr helpctx, handler_fn handler,
|
||||
intorptr context, intorptr context2)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);
|
||||
c->editbox.label = label ? dupstr(label) : NULL;
|
||||
c->editbox.shortcut = shortcut;
|
||||
c->editbox.percentwidth = percentage;
|
||||
c->editbox.password = false;
|
||||
c->editbox.has_list = false;
|
||||
c->editbox.context2 = context2;
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_combobox(struct controlset *s, const char *label,
|
||||
char shortcut, int percentage,
|
||||
intorptr helpctx, handler_fn handler,
|
||||
intorptr context, intorptr context2)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);
|
||||
c->editbox.label = label ? dupstr(label) : NULL;
|
||||
c->editbox.shortcut = shortcut;
|
||||
c->editbox.percentwidth = percentage;
|
||||
c->editbox.password = false;
|
||||
c->editbox.has_list = true;
|
||||
c->editbox.context2 = context2;
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* `ncolumns' is followed by (alternately) radio button titles and
|
||||
* intorptrs, until a NULL in place of a title string is seen. Each
|
||||
* title is expected to be followed by a shortcut _iff_ `shortcut'
|
||||
* is NO_SHORTCUT.
|
||||
*/
|
||||
union control *ctrl_radiobuttons(struct controlset *s, const char *label,
|
||||
char shortcut, int ncolumns, intorptr helpctx,
|
||||
handler_fn handler, intorptr context, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i;
|
||||
union control *c = ctrl_new(s, CTRL_RADIO, helpctx, handler, context);
|
||||
c->radio.label = label ? dupstr(label) : NULL;
|
||||
c->radio.shortcut = shortcut;
|
||||
c->radio.ncolumns = ncolumns;
|
||||
/*
|
||||
* Initial pass along variable argument list to count the
|
||||
* buttons.
|
||||
*/
|
||||
va_start(ap, context);
|
||||
i = 0;
|
||||
while (va_arg(ap, char *) != NULL) {
|
||||
i++;
|
||||
if (c->radio.shortcut == NO_SHORTCUT)
|
||||
(void)va_arg(ap, int); /* char promotes to int in arg lists */
|
||||
(void)va_arg(ap, intorptr);
|
||||
}
|
||||
va_end(ap);
|
||||
c->radio.nbuttons = i;
|
||||
if (c->radio.shortcut == NO_SHORTCUT)
|
||||
c->radio.shortcuts = snewn(c->radio.nbuttons, char);
|
||||
else
|
||||
c->radio.shortcuts = NULL;
|
||||
c->radio.buttons = snewn(c->radio.nbuttons, char *);
|
||||
c->radio.buttondata = snewn(c->radio.nbuttons, intorptr);
|
||||
/*
|
||||
* Second pass along variable argument list to actually fill in
|
||||
* the structure.
|
||||
*/
|
||||
va_start(ap, context);
|
||||
for (i = 0; i < c->radio.nbuttons; i++) {
|
||||
c->radio.buttons[i] = dupstr(va_arg(ap, char *));
|
||||
if (c->radio.shortcut == NO_SHORTCUT)
|
||||
c->radio.shortcuts[i] = va_arg(ap, int);
|
||||
/* char promotes to int in arg lists */
|
||||
c->radio.buttondata[i] = va_arg(ap, intorptr);
|
||||
}
|
||||
va_end(ap);
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_pushbutton(struct controlset *s, const char *label,
|
||||
char shortcut, intorptr helpctx,
|
||||
handler_fn handler, intorptr context)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_BUTTON, helpctx, handler, context);
|
||||
c->button.label = label ? dupstr(label) : NULL;
|
||||
c->button.shortcut = shortcut;
|
||||
c->button.isdefault = false;
|
||||
c->button.iscancel = false;
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_listbox(struct controlset *s, const char *label,
|
||||
char shortcut, intorptr helpctx,
|
||||
handler_fn handler, intorptr context)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
|
||||
c->listbox.label = label ? dupstr(label) : NULL;
|
||||
c->listbox.shortcut = shortcut;
|
||||
c->listbox.height = 5; /* *shrug* a plausible default */
|
||||
c->listbox.draglist = false;
|
||||
c->listbox.multisel = 0;
|
||||
c->listbox.percentwidth = 100;
|
||||
c->listbox.ncols = 0;
|
||||
c->listbox.percentages = NULL;
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_droplist(struct controlset *s, const char *label,
|
||||
char shortcut, int percentage, intorptr helpctx,
|
||||
handler_fn handler, intorptr context)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
|
||||
c->listbox.label = label ? dupstr(label) : NULL;
|
||||
c->listbox.shortcut = shortcut;
|
||||
c->listbox.height = 0; /* means it's a drop-down list */
|
||||
c->listbox.draglist = false;
|
||||
c->listbox.multisel = 0;
|
||||
c->listbox.percentwidth = percentage;
|
||||
c->listbox.ncols = 0;
|
||||
c->listbox.percentages = NULL;
|
||||
c->listbox.hscroll = false;
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_draglist(struct controlset *s, const char *label,
|
||||
char shortcut, intorptr helpctx,
|
||||
handler_fn handler, intorptr context)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
|
||||
c->listbox.label = label ? dupstr(label) : NULL;
|
||||
c->listbox.shortcut = shortcut;
|
||||
c->listbox.height = 5; /* *shrug* a plausible default */
|
||||
c->listbox.draglist = true;
|
||||
c->listbox.multisel = 0;
|
||||
c->listbox.percentwidth = 100;
|
||||
c->listbox.ncols = 0;
|
||||
c->listbox.percentages = NULL;
|
||||
c->listbox.hscroll = false;
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_filesel(struct controlset *s, const char *label,
|
||||
char shortcut, const char *filter, bool write,
|
||||
const char *title, intorptr helpctx,
|
||||
handler_fn handler, intorptr context)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_FILESELECT, helpctx, handler, context);
|
||||
c->fileselect.label = label ? dupstr(label) : NULL;
|
||||
c->fileselect.shortcut = shortcut;
|
||||
c->fileselect.filter = filter;
|
||||
c->fileselect.for_writing = write;
|
||||
c->fileselect.title = dupstr(title);
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_fontsel(struct controlset *s, const char *label,
|
||||
char shortcut, intorptr helpctx,
|
||||
handler_fn handler, intorptr context)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_FONTSELECT, helpctx, handler, context);
|
||||
c->fontselect.label = label ? dupstr(label) : NULL;
|
||||
c->fontselect.shortcut = shortcut;
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_tabdelay(struct controlset *s, union control *ctrl)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_TABDELAY, P(NULL), NULL, P(NULL));
|
||||
c->tabdelay.ctrl = ctrl;
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_text(struct controlset *s, const char *text,
|
||||
intorptr helpctx)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_TEXT, helpctx, NULL, P(NULL));
|
||||
c->text.label = dupstr(text);
|
||||
return c;
|
||||
}
|
||||
|
||||
union control *ctrl_checkbox(struct controlset *s, const char *label,
|
||||
char shortcut, intorptr helpctx,
|
||||
handler_fn handler, intorptr context)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_CHECKBOX, helpctx, handler, context);
|
||||
c->checkbox.label = label ? dupstr(label) : NULL;
|
||||
c->checkbox.shortcut = shortcut;
|
||||
return c;
|
||||
}
|
||||
|
||||
void ctrl_free(union control *ctrl)
|
||||
{
|
||||
int i;
|
||||
|
||||
sfree(ctrl->generic.label);
|
||||
switch (ctrl->generic.type) {
|
||||
case CTRL_RADIO:
|
||||
for (i = 0; i < ctrl->radio.nbuttons; i++)
|
||||
sfree(ctrl->radio.buttons[i]);
|
||||
sfree(ctrl->radio.buttons);
|
||||
sfree(ctrl->radio.shortcuts);
|
||||
sfree(ctrl->radio.buttondata);
|
||||
break;
|
||||
case CTRL_COLUMNS:
|
||||
sfree(ctrl->columns.percentages);
|
||||
break;
|
||||
case CTRL_LISTBOX:
|
||||
sfree(ctrl->listbox.percentages);
|
||||
break;
|
||||
case CTRL_FILESELECT:
|
||||
sfree(ctrl->fileselect.title);
|
||||
break;
|
||||
#ifdef MOD_ZMODEM
|
||||
case CTRL_DIRECTORYSELECT:
|
||||
sfree(ctrl->fileselect.title);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
sfree(ctrl);
|
||||
}
|
||||
|
||||
#ifdef MOD_ZMODEM
|
||||
union control *ctrl_directorysel(struct controlset *s,char *label,char shortcut,
|
||||
char *title,
|
||||
intorptr helpctx, handler_fn handler,
|
||||
intorptr context)
|
||||
{
|
||||
union control *c = ctrl_new(s, CTRL_DIRECTORYSELECT, helpctx, handler, context);
|
||||
c->fileselect.label = label ? dupstr(label) : NULL;
|
||||
c->fileselect.shortcut = shortcut;
|
||||
c->fileselect.title = dupstr(title);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
* Exports and types from dialog.c.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the big union which defines a single control, of any
|
||||
* type.
|
||||
*
|
||||
* General principles:
|
||||
* - _All_ pointers in this structure are expected to point to
|
||||
* dynamically allocated things, unless otherwise indicated.
|
||||
* - `char' fields giving keyboard shortcuts are expected to be
|
||||
* NO_SHORTCUT if no shortcut is desired for a particular control.
|
||||
* - The `label' field can often be NULL, which will cause the
|
||||
* control to not have a label at all. This doesn't apply to
|
||||
* checkboxes and push buttons, in which the label is not
|
||||
* separate from the control.
|
||||
*/
|
||||
|
||||
#define NO_SHORTCUT '\0'
|
||||
|
||||
enum {
|
||||
CTRL_TEXT, /* just a static line of text */
|
||||
CTRL_EDITBOX, /* label plus edit box */
|
||||
CTRL_RADIO, /* label plus radio buttons */
|
||||
CTRL_CHECKBOX, /* checkbox (contains own label) */
|
||||
CTRL_BUTTON, /* simple push button (no label) */
|
||||
CTRL_LISTBOX, /* label plus list box */
|
||||
CTRL_COLUMNS, /* divide window into columns */
|
||||
CTRL_FILESELECT, /* label plus filename selector */
|
||||
CTRL_FONTSELECT, /* label plus font selector */
|
||||
#ifdef MOD_ZMODEM
|
||||
CTRL_DIRECTORYSELECT, /* label plus directory selector */
|
||||
#endif
|
||||
CTRL_TABDELAY /* see `tabdelay' below */
|
||||
};
|
||||
|
||||
/*
|
||||
* Many controls have `intorptr' unions for storing user data,
|
||||
* since the user might reasonably want to store either an integer
|
||||
* or a void * pointer. Here I define a union, and two convenience
|
||||
* functions to create that union from actual integers or pointers.
|
||||
*
|
||||
* The convenience functions are declared as inline if possible.
|
||||
* Otherwise, they're declared here and defined when this header is
|
||||
* included with DEFINE_INTORPTR_FNS defined. This is a total pain,
|
||||
* but such is life.
|
||||
*/
|
||||
typedef union { void *p; int i; } intorptr;
|
||||
|
||||
#ifndef INLINE
|
||||
intorptr I(int i);
|
||||
intorptr P(void *p);
|
||||
#endif
|
||||
|
||||
#if defined DEFINE_INTORPTR_FNS || defined INLINE
|
||||
#ifdef INLINE
|
||||
#define PREFIX INLINE
|
||||
#else
|
||||
#define PREFIX
|
||||
#endif
|
||||
PREFIX intorptr I(int i) { intorptr ret; ret.i = i; return ret; }
|
||||
PREFIX intorptr P(void *p) { intorptr ret; ret.p = p; return ret; }
|
||||
#undef PREFIX
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Each control has an `int' field specifying which columns it
|
||||
* occupies in a multi-column part of the dialog box. These macros
|
||||
* pack and unpack that field.
|
||||
*
|
||||
* If a control belongs in exactly one column, just specifying the
|
||||
* column number is perfectly adequate.
|
||||
*/
|
||||
#define COLUMN_FIELD(start, span) ( (((span)-1) << 16) + (start) )
|
||||
#define COLUMN_START(field) ( (field) & 0xFFFF )
|
||||
#define COLUMN_SPAN(field) ( (((field) >> 16) & 0xFFFF) + 1 )
|
||||
|
||||
union control;
|
||||
|
||||
/*
|
||||
* The number of event types is being deliberately kept small, on
|
||||
* the grounds that not all platforms might be able to report a
|
||||
* large number of subtle events. We have:
|
||||
* - the special REFRESH event, called when a control's value
|
||||
* needs setting
|
||||
* - the ACTION event, called when the user does something that
|
||||
* positively requests action (double-clicking a list box item,
|
||||
* or pushing a push-button)
|
||||
* - the VALCHANGE event, called when the user alters the setting
|
||||
* of the control in a way that is usually considered to alter
|
||||
* the underlying data (toggling a checkbox or radio button,
|
||||
* moving the items around in a drag-list, editing an edit
|
||||
* control)
|
||||
* - the SELCHANGE event, called when the user alters the setting
|
||||
* of the control in a more minor way (changing the selected
|
||||
* item in a list box).
|
||||
* - the CALLBACK event, which happens after the handler routine
|
||||
* has requested a subdialog (file selector, font selector,
|
||||
* colour selector) and it has come back with information.
|
||||
*/
|
||||
enum {
|
||||
EVENT_REFRESH,
|
||||
EVENT_ACTION,
|
||||
EVENT_VALCHANGE,
|
||||
EVENT_SELCHANGE,
|
||||
EVENT_CALLBACK
|
||||
};
|
||||
typedef void (*handler_fn)(union control *ctrl, dlgparam *dp,
|
||||
void *data, int event);
|
||||
|
||||
#define STANDARD_PREFIX \
|
||||
int type; \
|
||||
char *label; \
|
||||
bool tabdelay; \
|
||||
int column; \
|
||||
handler_fn handler; \
|
||||
intorptr context; \
|
||||
intorptr helpctx; \
|
||||
union control *align_next_to
|
||||
|
||||
union control {
|
||||
/*
|
||||
* The first possibility in this union is the generic header
|
||||
* shared by all the structures, which we are therefore allowed
|
||||
* to access through any one of them.
|
||||
*/
|
||||
struct {
|
||||
int type;
|
||||
/*
|
||||
* Every control except CTRL_COLUMNS has _some_ sort of
|
||||
* label. By putting it in the `generic' union as well as
|
||||
* everywhere else, we avoid having to have an irritating
|
||||
* switch statement when we go through and deallocate all
|
||||
* the memory in a config-box structure.
|
||||
*
|
||||
* Yes, this does mean that any non-NULL value in this
|
||||
* field is expected to be dynamically allocated and
|
||||
* freeable.
|
||||
*
|
||||
* For CTRL_COLUMNS, this field MUST be NULL.
|
||||
*/
|
||||
char *label;
|
||||
/*
|
||||
* If `tabdelay' is non-zero, it indicates that this
|
||||
* particular control should not yet appear in the tab
|
||||
* order. A subsequent CTRL_TABDELAY entry will place it.
|
||||
*/
|
||||
bool tabdelay;
|
||||
/*
|
||||
* Indicate which column(s) this control occupies. This can
|
||||
* be unpacked into starting column and column span by the
|
||||
* COLUMN macros above.
|
||||
*/
|
||||
int column;
|
||||
/*
|
||||
* Most controls need to provide a function which gets
|
||||
* called when that control's setting is changed, or when
|
||||
* the control's setting needs initialising.
|
||||
*
|
||||
* The `data' parameter points to the writable data being
|
||||
* modified as a result of the configuration activity; for
|
||||
* example, the PuTTY `Conf' structure, although not
|
||||
* necessarily.
|
||||
*
|
||||
* The `dlg' parameter is passed back to the platform-
|
||||
* specific routines to read and write the actual control
|
||||
* state.
|
||||
*/
|
||||
handler_fn handler;
|
||||
/*
|
||||
* Almost all of the above functions will find it useful to
|
||||
* be able to store a piece of `void *' or `int' data.
|
||||
*/
|
||||
intorptr context;
|
||||
/*
|
||||
* For any control, we also allow the storage of a piece of
|
||||
* data for use by context-sensitive help. For example, on
|
||||
* Windows you can click the magic question mark and then
|
||||
* click a control, and help for that control should spring
|
||||
* up. Hence, here is a slot in which to store per-control
|
||||
* data that a particular platform-specific driver can use
|
||||
* to ensure it brings up the right piece of help text.
|
||||
*/
|
||||
intorptr helpctx;
|
||||
/*
|
||||
* Setting this to non-NULL coerces two controls to have their
|
||||
* y-coordinates adjusted so that they can sit alongside each
|
||||
* other and look nicely aligned, even if they're different
|
||||
* heights.
|
||||
*
|
||||
* Set this field on the _second_ control of the pair (in
|
||||
* terms of order in the data structure), so that when it's
|
||||
* instantiated, the first one is already there to be referred
|
||||
* to.
|
||||
*/
|
||||
union control *align_next_to;
|
||||
} generic;
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
union control *ctrl;
|
||||
} tabdelay;
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
} text;
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
char shortcut; /* keyboard shortcut */
|
||||
/*
|
||||
* Percentage of the dialog-box width used by the edit box.
|
||||
* If this is set to 100, the label is on its own line;
|
||||
* otherwise the label is on the same line as the box
|
||||
* itself.
|
||||
*/
|
||||
int percentwidth;
|
||||
bool password; /* details of input are hidden */
|
||||
/*
|
||||
* A special case of the edit box is the combo box, which
|
||||
* has a drop-down list built in. (Note that a _non_-
|
||||
* editable drop-down list is done as a special case of a
|
||||
* list box.)
|
||||
*
|
||||
* Don't try setting has_list and password on the same
|
||||
* control; front ends are not required to support that
|
||||
* combination.
|
||||
*/
|
||||
bool has_list;
|
||||
/*
|
||||
* Edit boxes tend to need two items of context, so here's
|
||||
* a spare.
|
||||
*/
|
||||
intorptr context2;
|
||||
} editbox;
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
/*
|
||||
* `shortcut' here is a single keyboard shortcut which is
|
||||
* expected to select the whole group of radio buttons. It
|
||||
* can be NO_SHORTCUT if required, and there is also a way
|
||||
* to place individual shortcuts on each button; see below.
|
||||
*/
|
||||
char shortcut;
|
||||
/*
|
||||
* There are separate fields for `ncolumns' and `nbuttons'
|
||||
* for several reasons.
|
||||
*
|
||||
* Firstly, we sometimes want the last of a set of buttons
|
||||
* to have a longer label than the rest; we achieve this by
|
||||
* setting `ncolumns' higher than `nbuttons', and the
|
||||
* layout code is expected to understand that the final
|
||||
* button should be given all the remaining space on the
|
||||
* line. This sounds like a ludicrously specific special
|
||||
* case (if we're doing this sort of thing, why not have
|
||||
* the general ability to have a particular button span
|
||||
* more than one column whether it's the last one or not?)
|
||||
* but actually it's reasonably common for the sort of
|
||||
* three-way control you get a lot of in PuTTY: `yes'
|
||||
* versus `no' versus `some more complex way to decide'.
|
||||
*
|
||||
* Secondly, setting `nbuttons' higher than `ncolumns' lets
|
||||
* us have more than one line of radio buttons for a single
|
||||
* setting. A very important special case of this is
|
||||
* setting `ncolumns' to 1, so that each button is on its
|
||||
* own line.
|
||||
*/
|
||||
int ncolumns;
|
||||
int nbuttons;
|
||||
/*
|
||||
* This points to a dynamically allocated array of `char *'
|
||||
* pointers, each of which points to a dynamically
|
||||
* allocated string.
|
||||
*/
|
||||
char **buttons; /* `nbuttons' button labels */
|
||||
/*
|
||||
* This points to a dynamically allocated array of `char'
|
||||
* giving the individual keyboard shortcuts for each radio
|
||||
* button. The array may be NULL if none are required.
|
||||
*/
|
||||
char *shortcuts; /* `nbuttons' shortcuts; may be NULL */
|
||||
/*
|
||||
* This points to a dynamically allocated array of
|
||||
* intorptr, giving helpful data for each button.
|
||||
*/
|
||||
intorptr *buttondata; /* `nbuttons' entries; may be NULL */
|
||||
} radio;
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
char shortcut;
|
||||
} checkbox;
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
char shortcut;
|
||||
/*
|
||||
* At least Windows has the concept of a `default push
|
||||
* button', which gets implicitly pressed when you hit
|
||||
* Return even if it doesn't have the input focus.
|
||||
*/
|
||||
bool isdefault;
|
||||
/*
|
||||
* Also, the reverse of this: a default cancel-type button,
|
||||
* which is implicitly pressed when you hit Escape.
|
||||
*/
|
||||
bool iscancel;
|
||||
} button;
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
char shortcut; /* keyboard shortcut */
|
||||
/*
|
||||
* Height of the list box, in approximate number of lines.
|
||||
* If this is zero, the list is a drop-down list.
|
||||
*/
|
||||
int height; /* height in lines */
|
||||
/*
|
||||
* If this is set, the list elements can be reordered by
|
||||
* the user (by drag-and-drop or by Up and Down buttons,
|
||||
* whatever the per-platform implementation feels
|
||||
* comfortable with). This is not guaranteed to work on a
|
||||
* drop-down list, so don't try it!
|
||||
*/
|
||||
bool draglist;
|
||||
/*
|
||||
* If this is non-zero, the list can have more than one
|
||||
* element selected at a time. This is not guaranteed to
|
||||
* work on a drop-down list, so don't try it!
|
||||
*
|
||||
* Different non-zero values request slightly different
|
||||
* types of multi-selection (this may well be meaningful
|
||||
* only in GTK, so everyone else can ignore it if they
|
||||
* want). 1 means the list box expects to have individual
|
||||
* items selected, whereas 2 means it expects the user to
|
||||
* want to select a large contiguous range at a time.
|
||||
*/
|
||||
int multisel;
|
||||
/*
|
||||
* Percentage of the dialog-box width used by the list box.
|
||||
* If this is set to 100, the label is on its own line;
|
||||
* otherwise the label is on the same line as the box
|
||||
* itself. Setting this to anything other than 100 is not
|
||||
* guaranteed to work on a _non_-drop-down list, so don't
|
||||
* try it!
|
||||
*/
|
||||
int percentwidth;
|
||||
/*
|
||||
* Some list boxes contain strings that contain tab
|
||||
* characters. If `ncols' is greater than 0, then
|
||||
* `percentages' is expected to be non-zero and to contain
|
||||
* the respective widths of `ncols' columns, which together
|
||||
* will exactly fit the width of the list box. Otherwise
|
||||
* `percentages' must be NULL.
|
||||
*
|
||||
* There should never be more than one column in a
|
||||
* drop-down list (one with height==0), because front ends
|
||||
* may have to implement it as a special case of an
|
||||
* editable combo box.
|
||||
*/
|
||||
int ncols; /* number of columns */
|
||||
int *percentages; /* % width of each column */
|
||||
/*
|
||||
* Flag which can be set to false to suppress the horizontal
|
||||
* scroll bar if a list box entry goes off the right-hand
|
||||
* side.
|
||||
*/
|
||||
bool hscroll;
|
||||
} listbox;
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
char shortcut;
|
||||
/*
|
||||
* `filter' dictates what type of files will be selected by
|
||||
* default; for example, when selecting private key files
|
||||
* the file selector would do well to only show .PPK files
|
||||
* (on those systems where this is the chosen extension).
|
||||
*
|
||||
* The precise contents of `filter' are platform-defined,
|
||||
* unfortunately. The special value NULL means `all files'
|
||||
* and is always a valid fallback.
|
||||
*
|
||||
* Unlike almost all strings in this structure, this value
|
||||
* is NOT expected to require freeing (although of course
|
||||
* you can always use ctrl_alloc if you do need to create
|
||||
* one on the fly). This is because the likely mode of use
|
||||
* is to define string constants in a platform-specific
|
||||
* header file, and directly reference those. Or worse, a
|
||||
* particular platform might choose to cast integers into
|
||||
* this pointer type...
|
||||
*/
|
||||
char const *filter;
|
||||
/*
|
||||
* Some systems like to know whether a file selector is
|
||||
* choosing a file to read or one to write (and possibly
|
||||
* create).
|
||||
*/
|
||||
bool for_writing;
|
||||
/*
|
||||
* On at least some platforms, the file selector is a
|
||||
* separate dialog box, and contains a user-settable title.
|
||||
*
|
||||
* This value _is_ expected to require freeing.
|
||||
*/
|
||||
char *title;
|
||||
} fileselect;
|
||||
struct {
|
||||
/* In this variant, `label' MUST be NULL. */
|
||||
STANDARD_PREFIX;
|
||||
int ncols; /* number of columns */
|
||||
int *percentages; /* % width of each column */
|
||||
/*
|
||||
* Every time this control type appears, exactly one of
|
||||
* `ncols' and the previous number of columns MUST be one.
|
||||
* Attempting to allow a seamless transition from a four-
|
||||
* to a five-column layout, for example, would be way more
|
||||
* trouble than it was worth. If you must lay things out
|
||||
* like that, define eight unevenly sized columns and use
|
||||
* column-spanning a lot. But better still, just don't.
|
||||
*
|
||||
* `percentages' may be NULL if ncols==1, to save space.
|
||||
*/
|
||||
} columns;
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
char shortcut;
|
||||
} fontselect;
|
||||
#ifdef MOD_ZMODEM
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
char shortcut;
|
||||
/*
|
||||
* On at least some platforms, the file selector is a
|
||||
* separate dialog box, and contains a user-settable title.
|
||||
*
|
||||
* This value _is_ expected to require freeing.
|
||||
*/
|
||||
char *title;
|
||||
} directoryselect;
|
||||
#endif
|
||||
};
|
||||
|
||||
#undef STANDARD_PREFIX
|
||||
|
||||
/*
|
||||
* `controlset' is a container holding an array of `union control'
|
||||
* structures, together with a panel name and a title for the whole
|
||||
* set. In Windows and any similar-looking GUI, each `controlset'
|
||||
* in the config will be a container box within a panel.
|
||||
*
|
||||
* Special case: if `boxname' is NULL, the control set gives an
|
||||
* overall title for an entire panel of controls.
|
||||
*/
|
||||
struct controlset {
|
||||
char *pathname; /* panel path, e.g. "SSH/Tunnels" */
|
||||
char *boxname; /* internal short name of controlset */
|
||||
char *boxtitle; /* title of container box */
|
||||
int ncolumns; /* current no. of columns at bottom */
|
||||
size_t ncontrols; /* number of `union control' in array */
|
||||
size_t ctrlsize; /* allocated size of array */
|
||||
union control **ctrls; /* actual array */
|
||||
};
|
||||
|
||||
typedef void (*ctrl_freefn_t)(void *); /* used by ctrl_alloc_with_free */
|
||||
|
||||
/*
|
||||
* This is the container structure which holds a complete set of
|
||||
* controls.
|
||||
*/
|
||||
struct controlbox {
|
||||
size_t nctrlsets; /* number of ctrlsets */
|
||||
size_t ctrlsetsize; /* ctrlset size */
|
||||
struct controlset **ctrlsets; /* actual array of ctrlsets */
|
||||
size_t nfrees;
|
||||
size_t freesize;
|
||||
void **frees; /* array of aux data areas to free */
|
||||
ctrl_freefn_t *freefuncs; /* parallel array of free functions */
|
||||
};
|
||||
|
||||
struct controlbox *ctrl_new_box(void);
|
||||
void ctrl_free_box(struct controlbox *);
|
||||
|
||||
/*
|
||||
* Standard functions used for populating a controlbox structure.
|
||||
*/
|
||||
|
||||
/* Set up a panel title. */
|
||||
struct controlset *ctrl_settitle(struct controlbox *,
|
||||
const char *path, const char *title);
|
||||
/* Retrieve a pointer to a controlset, creating it if absent. */
|
||||
struct controlset *ctrl_getset(struct controlbox *, const char *path,
|
||||
const char *name, const char *boxtitle);
|
||||
void ctrl_free_set(struct controlset *);
|
||||
|
||||
void ctrl_free(union control *);
|
||||
|
||||
/*
|
||||
* This function works like `malloc', but the memory it returns
|
||||
* will be automatically freed when the controlbox is freed. Note
|
||||
* that a controlbox is a dialog-box _template_, not an instance,
|
||||
* and so data allocated through this function is better not used
|
||||
* to hold modifiable per-instance things. It's mostly here for
|
||||
* allocating structures to be passed as control handler params.
|
||||
*
|
||||
* ctrl_alloc_with_free also allows you to provide a function to free
|
||||
* the structure, in case there are other dynamically allocated bits
|
||||
* and pieces dangling off it.
|
||||
*/
|
||||
void *ctrl_alloc(struct controlbox *b, size_t size);
|
||||
void *ctrl_alloc_with_free(struct controlbox *b, size_t size,
|
||||
ctrl_freefn_t freefunc);
|
||||
|
||||
/*
|
||||
* Individual routines to create `union control' structures in a controlset.
|
||||
*
|
||||
* Most of these routines allow the most common fields to be set
|
||||
* directly, and put default values in the rest. Each one returns a
|
||||
* pointer to the `union control' it created, so that final tweaks
|
||||
* can be made.
|
||||
*/
|
||||
|
||||
/* `ncolumns' is followed by that many percentages, as integers. */
|
||||
union control *ctrl_columns(struct controlset *, int ncolumns, ...);
|
||||
union control *ctrl_editbox(struct controlset *, const char *label,
|
||||
char shortcut, int percentage, intorptr helpctx,
|
||||
handler_fn handler,
|
||||
intorptr context, intorptr context2);
|
||||
union control *ctrl_combobox(struct controlset *, const char *label,
|
||||
char shortcut, int percentage, intorptr helpctx,
|
||||
handler_fn handler,
|
||||
intorptr context, intorptr context2);
|
||||
/*
|
||||
* `ncolumns' is followed by (alternately) radio button titles and
|
||||
* intorptrs, until a NULL in place of a title string is seen. Each
|
||||
* title is expected to be followed by a shortcut _iff_ `shortcut'
|
||||
* is NO_SHORTCUT.
|
||||
*/
|
||||
union control *ctrl_radiobuttons(struct controlset *, const char *label,
|
||||
char shortcut, int ncolumns, intorptr helpctx,
|
||||
handler_fn handler, intorptr context, ...);
|
||||
union control *ctrl_pushbutton(struct controlset *, const char *label,
|
||||
char shortcut, intorptr helpctx,
|
||||
handler_fn handler, intorptr context);
|
||||
union control *ctrl_listbox(struct controlset *, const char *label,
|
||||
char shortcut, intorptr helpctx,
|
||||
handler_fn handler, intorptr context);
|
||||
union control *ctrl_droplist(struct controlset *, const char *label,
|
||||
char shortcut, int percentage, intorptr helpctx,
|
||||
handler_fn handler, intorptr context);
|
||||
union control *ctrl_draglist(struct controlset *, const char *label,
|
||||
char shortcut, intorptr helpctx,
|
||||
handler_fn handler, intorptr context);
|
||||
union control *ctrl_filesel(struct controlset *, const char *label,
|
||||
char shortcut, const char *filter, bool write,
|
||||
const char *title, intorptr helpctx,
|
||||
handler_fn handler, intorptr context);
|
||||
union control *ctrl_fontsel(struct controlset *, const char *label,
|
||||
char shortcut, intorptr helpctx,
|
||||
handler_fn handler, intorptr context);
|
||||
union control *ctrl_text(struct controlset *, const char *text,
|
||||
intorptr helpctx);
|
||||
union control *ctrl_checkbox(struct controlset *, const char *label,
|
||||
char shortcut, intorptr helpctx,
|
||||
handler_fn handler, intorptr context);
|
||||
union control *ctrl_tabdelay(struct controlset *, union control *);
|
||||
#ifdef MOD_ZMODEM
|
||||
union control *ctrl_directorysel(struct controlset *,char *label,char shortcut,
|
||||
char *title,
|
||||
intorptr helpctx,
|
||||
handler_fn handler, intorptr context);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Routines the platform-independent dialog code can call to read
|
||||
* and write the values of controls.
|
||||
*/
|
||||
void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int whichbutton);
|
||||
int dlg_radiobutton_get(union control *ctrl, dlgparam *dp);
|
||||
void dlg_checkbox_set(union control *ctrl, dlgparam *dp, bool checked);
|
||||
bool dlg_checkbox_get(union control *ctrl, dlgparam *dp);
|
||||
void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text);
|
||||
char *dlg_editbox_get(union control *ctrl, dlgparam *dp); /* result must be freed by caller */
|
||||
/* The `listbox' functions can also apply to combo boxes. */
|
||||
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);
|
||||
/*
|
||||
* Each listbox entry may have a numeric id associated with it.
|
||||
* Note that some front ends only permit a string to be stored at
|
||||
* each position, which means that _if_ you put two identical
|
||||
* strings in any listbox then you MUST not assign them different
|
||||
* IDs and expect to get meaningful results back.
|
||||
*/
|
||||
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);
|
||||
/* dlg_listbox_index returns <0 if no single element is selected. */
|
||||
int dlg_listbox_index(union control *ctrl, dlgparam *dp);
|
||||
bool dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index);
|
||||
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);
|
||||
void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fn);
|
||||
FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp);
|
||||
#ifdef MOD_ZMODEM
|
||||
void dlg_directorysel_set(union control *ctrl, void *dlg, Filename fn);
|
||||
void dlg_directorysel_get(union control *ctrl, void *dlg, Filename *fn);
|
||||
#endif
|
||||
/*
|
||||
* Bracketing a large set of updates in these two functions will
|
||||
* cause the front end (if possible) to delay updating the screen
|
||||
* until it's all complete, thus avoiding flicker.
|
||||
*/
|
||||
void dlg_update_start(union control *ctrl, dlgparam *dp);
|
||||
void dlg_update_done(union control *ctrl, dlgparam *dp);
|
||||
/*
|
||||
* Set input focus into a particular control.
|
||||
*/
|
||||
void dlg_set_focus(union control *ctrl, dlgparam *dp);
|
||||
/*
|
||||
* Change the label text on a control.
|
||||
*/
|
||||
void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text);
|
||||
/*
|
||||
* Return the `ctrl' structure for the most recent control that had
|
||||
* the input focus apart from the one mentioned. This is NOT
|
||||
* GUARANTEED to work on all platforms, so don't base any critical
|
||||
* functionality on it!
|
||||
*/
|
||||
union control *dlg_last_focused(union control *ctrl, dlgparam *dp);
|
||||
/*
|
||||
* Find out whether a particular control is currently visible.
|
||||
*/
|
||||
bool dlg_is_visible(union control *ctrl, dlgparam *dp);
|
||||
/*
|
||||
* During event processing, you might well want to give an error
|
||||
* indication to the user. dlg_beep() is a quick and easy generic
|
||||
* error; dlg_error() puts up a message-box or equivalent.
|
||||
*/
|
||||
void dlg_beep(dlgparam *dp);
|
||||
void dlg_error_msg(dlgparam *dp, const char *msg);
|
||||
/*
|
||||
* This function signals to the front end that the dialog's
|
||||
* processing is completed, and passes an integer value (typically
|
||||
* a success status).
|
||||
*/
|
||||
void dlg_end(dlgparam *dp, int value);
|
||||
|
||||
/*
|
||||
* Routines to manage a (per-platform) colour selector.
|
||||
* dlg_coloursel_start() is called in an event handler, and
|
||||
* schedules the running of a colour selector after the event
|
||||
* handler returns. The colour selector will send EVENT_CALLBACK to
|
||||
* the control that spawned it, when it's finished;
|
||||
* dlg_coloursel_results() fetches the results, as integers from 0
|
||||
* to 255; it returns nonzero on success, or zero if the colour
|
||||
* selector was dismissed by hitting Cancel or similar.
|
||||
*
|
||||
* dlg_coloursel_start() accepts an RGB triple which is used to
|
||||
* initialise the colour selector to its starting 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);
|
||||
|
||||
/*
|
||||
* This routine is used by the platform-independent code to
|
||||
* indicate that the value of a particular control is likely to
|
||||
* have changed. It triggers a call of the handler for that control
|
||||
* with `event' set to EVENT_REFRESH.
|
||||
*
|
||||
* If `ctrl' is NULL, _all_ controls in the dialog get refreshed
|
||||
* (for loading or saving entire sets of settings).
|
||||
*/
|
||||
void dlg_refresh(union control *ctrl, dlgparam *dp);
|
||||
|
||||
/*
|
||||
* Standard helper functions for reading a controlbox structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Find the index of next controlset in a controlbox for a given
|
||||
* path, or -1 if no such controlset exists. If -1 is passed as
|
||||
* input, finds the first. Intended usage is something like
|
||||
*
|
||||
* for (index=-1; (index=ctrl_find_path(ctrlbox, index, path)) >= 0 ;) {
|
||||
* ... process this controlset ...
|
||||
* }
|
||||
*/
|
||||
int ctrl_find_path(struct controlbox *b, const char *path, int index);
|
||||
int ctrl_path_elements(const char *path);
|
||||
/* Return the number of matching path elements at the starts of p1 and p2,
|
||||
* or INT_MAX if the paths are identical. */
|
||||
int ctrl_path_compare(const char *p1, const char *p2);
|
||||
|
||||
#ifdef MOD_ZMODEM
|
||||
/*
|
||||
* The standard directory-selector handler expects the main `context'
|
||||
* field to contain the `offsetof' a Filename field in the
|
||||
* structure pointed to by `data'.
|
||||
*/
|
||||
void conf_directorysel_handler(union control *ctrl, void *dlg, void *data, int event);
|
||||
#endif
|
||||
+1167
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,243 @@
|
||||
#ifndef PUTTY_ECC_H
|
||||
#define PUTTY_ECC_H
|
||||
|
||||
/*
|
||||
* Arithmetic functions for the various kinds of elliptic curves used
|
||||
* by PuTTY's public-key cryptography.
|
||||
*
|
||||
* All of these elliptic curves are over the finite field whose order
|
||||
* is a large prime p. (Elliptic curves over a field of order 2^n are
|
||||
* also known, but PuTTY currently has no need of them.)
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Weierstrass curves (or rather, 'short form' Weierstrass curves).
|
||||
*
|
||||
* A curve in this form is defined by two parameters a,b, and the
|
||||
* non-identity points on the curve are represented by (x,y) (the
|
||||
* 'affine coordinates') such that y^2 = x^3 + ax + b.
|
||||
*
|
||||
* The identity element of the curve's group is an additional 'point
|
||||
* at infinity', which is considered to be the third point on the
|
||||
* intersection of the curve with any vertical line. Hence, the
|
||||
* inverse of the point (x,y) is (x,-y).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Create and destroy Weierstrass curve data structures. The mandatory
|
||||
* parameters to the constructor are the prime modulus p, and the
|
||||
* curve parameters a,b.
|
||||
*
|
||||
* 'nonsquare_mod_p' is an optional extra parameter, only needed by
|
||||
* ecc_edwards_point_new_from_y which has to take a modular square
|
||||
* root. You can pass it as NULL if you don't need that function.
|
||||
*/
|
||||
WeierstrassCurve *ecc_weierstrass_curve(
|
||||
mp_int *p, mp_int *a, mp_int *b, mp_int *nonsquare_mod_p);
|
||||
void ecc_weierstrass_curve_free(WeierstrassCurve *);
|
||||
|
||||
/*
|
||||
* Create points on a Weierstrass curve, given the curve.
|
||||
*
|
||||
* point_new_identity returns the special identity point.
|
||||
* point_new(x,y) returns the non-identity point with the given affine
|
||||
* coordinates.
|
||||
*
|
||||
* point_new_from_x constructs a non-identity point given only the
|
||||
* x-coordinate, by using the curve equation to work out what y has to
|
||||
* be. Of course the equation only tells you y^2, so it only
|
||||
* determines y up to sign; the parameter desired_y_parity controls
|
||||
* which of the two values of y you get, by saying whether you'd like
|
||||
* its minimal non-negative residue mod p to be even or odd. (Of
|
||||
* course, since p itself is odd, exactly one of y and p-y is odd.)
|
||||
* This function has to take a modular square root, so it will only
|
||||
* work if you passed in a non-square mod p when constructing the
|
||||
* curve.
|
||||
*/
|
||||
WeierstrassPoint *ecc_weierstrass_point_new_identity(WeierstrassCurve *curve);
|
||||
WeierstrassPoint *ecc_weierstrass_point_new(
|
||||
WeierstrassCurve *curve, mp_int *x, mp_int *y);
|
||||
WeierstrassPoint *ecc_weierstrass_point_new_from_x(
|
||||
WeierstrassCurve *curve, mp_int *x, unsigned desired_y_parity);
|
||||
|
||||
/* Memory management: copy and free points. */
|
||||
void ecc_weierstrass_point_copy_into(
|
||||
WeierstrassPoint *dest, WeierstrassPoint *src);
|
||||
WeierstrassPoint *ecc_weierstrass_point_copy(WeierstrassPoint *wc);
|
||||
void ecc_weierstrass_point_free(WeierstrassPoint *point);
|
||||
|
||||
/* Check whether a point is actually on the curve. */
|
||||
unsigned ecc_weierstrass_point_valid(WeierstrassPoint *);
|
||||
|
||||
/*
|
||||
* Add two points and return their sum. This function is fully
|
||||
* general: it should do the right thing if the two inputs are the
|
||||
* same, or if either (or both) of the input points is the identity,
|
||||
* or if the two input points are inverses so the output is the
|
||||
* identity. However, it pays for that generality by being slower than
|
||||
* the special-purpose functions below..
|
||||
*/
|
||||
WeierstrassPoint *ecc_weierstrass_add_general(
|
||||
WeierstrassPoint *, WeierstrassPoint *);
|
||||
|
||||
/*
|
||||
* Fast but less general arithmetic functions: add two points on the
|
||||
* condition that they are not equal and neither is the identity, and
|
||||
* add a point to itself.
|
||||
*/
|
||||
WeierstrassPoint *ecc_weierstrass_add(WeierstrassPoint *, WeierstrassPoint *);
|
||||
WeierstrassPoint *ecc_weierstrass_double(WeierstrassPoint *);
|
||||
|
||||
/*
|
||||
* Compute an integer multiple of a point. Not guaranteed to work
|
||||
* unless the integer argument is less than the order of the point in
|
||||
* the group (because it won't cope if an identity element shows up in
|
||||
* any intermediate product).
|
||||
*/
|
||||
WeierstrassPoint *ecc_weierstrass_multiply(WeierstrassPoint *, mp_int *);
|
||||
|
||||
/*
|
||||
* Query functions to get the value of a point back out. is_identity
|
||||
* tells you whether the point is the identity; if it isn't, then
|
||||
* get_affine will retrieve one or both of its affine coordinates.
|
||||
* (You can pass NULL as either output pointer, if you don't need that
|
||||
* coordinate as output.)
|
||||
*/
|
||||
unsigned ecc_weierstrass_is_identity(WeierstrassPoint *wp);
|
||||
void ecc_weierstrass_get_affine(WeierstrassPoint *wp, mp_int **x, mp_int **y);
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Montgomery curves.
|
||||
*
|
||||
* A curve in this form is defined by two parameters a,b, and the
|
||||
* curve equation is by^2 = x^3 + ax^2 + x.
|
||||
*
|
||||
* As with Weierstrass curves, there's an additional point at infinity
|
||||
* that is the identity element, and the inverse of (x,y) is (x,-y).
|
||||
*
|
||||
* However, we don't actually work with full (x,y) pairs. We just
|
||||
* store the x-coordinate (so what we're really representing is not a
|
||||
* specific point on the curve but a two-point set {P,-P}). This means
|
||||
* you can't quite do point addition, because if you're given {P,-P}
|
||||
* and {Q,-Q} as input, you can work out a pair of x-coordinates that
|
||||
* are those of P-Q and P+Q, but you don't know which is which.
|
||||
*
|
||||
* Instead, the basic operation is 'differential addition', in which
|
||||
* you are given three parameters P, Q and P-Q and you return P+Q. (As
|
||||
* well as disambiguating which of the possible answers you want, that
|
||||
* extra input also enables a fast formulae for computing it. This
|
||||
* fast formula is more or less why Montgomery curves are useful in
|
||||
* the first place.)
|
||||
*
|
||||
* Doubling a point is still possible to do unambiguously, so you can
|
||||
* still compute an integer multiple of P if you start by making 2P
|
||||
* and then doing a series of differential additions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Create and destroy Montgomery curve data structures.
|
||||
*/
|
||||
MontgomeryCurve *ecc_montgomery_curve(mp_int *p, mp_int *a, mp_int *b);
|
||||
void ecc_montgomery_curve_free(MontgomeryCurve *);
|
||||
|
||||
/*
|
||||
* Create, copy and free points on the curve. We don't need to
|
||||
* explicitly represent the identity for this application.
|
||||
*/
|
||||
MontgomeryPoint *ecc_montgomery_point_new(MontgomeryCurve *mc, mp_int *x);
|
||||
void ecc_montgomery_point_copy_into(
|
||||
MontgomeryPoint *dest, MontgomeryPoint *src);
|
||||
MontgomeryPoint *ecc_montgomery_point_copy(MontgomeryPoint *orig);
|
||||
void ecc_montgomery_point_free(MontgomeryPoint *mp);
|
||||
|
||||
/*
|
||||
* Basic arithmetic routines: differential addition and point-
|
||||
* doubling. Each of these assumes that no special cases come up - no
|
||||
* input or output point should be the identity, and in diff_add, P
|
||||
* and Q shouldn't be the same.
|
||||
*/
|
||||
MontgomeryPoint *ecc_montgomery_diff_add(
|
||||
MontgomeryPoint *P, MontgomeryPoint *Q, MontgomeryPoint *PminusQ);
|
||||
MontgomeryPoint *ecc_montgomery_double(MontgomeryPoint *P);
|
||||
|
||||
/*
|
||||
* Compute an integer multiple of a point.
|
||||
*/
|
||||
MontgomeryPoint *ecc_montgomery_multiply(MontgomeryPoint *, mp_int *);
|
||||
|
||||
/*
|
||||
* Return the affine x-coordinate of a point.
|
||||
*/
|
||||
void ecc_montgomery_get_affine(MontgomeryPoint *mp, mp_int **x);
|
||||
|
||||
/*
|
||||
* Test whether a point is the curve identity.
|
||||
*/
|
||||
unsigned ecc_montgomery_is_identity(MontgomeryPoint *mp);
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Twisted Edwards curves.
|
||||
*
|
||||
* A curve in this form is defined by two parameters d,a, and the
|
||||
* curve equation is a x^2 + y^2 = 1 + d x^2 y^2.
|
||||
*
|
||||
* Apparently if you ask a proper algebraic geometer they'll tell you
|
||||
* that this is technically not an actual elliptic curve. Certainly it
|
||||
* doesn't work quite the same way as the other kinds: in this form,
|
||||
* there is no need for a point at infinity, because the identity
|
||||
* element is represented by the affine coordinates (0,1). And you
|
||||
* invert a point by negating its x rather than y coordinate: the
|
||||
* inverse of (x,y) is (-x,y).
|
||||
*
|
||||
* The usefulness of this representation is that the addition formula
|
||||
* is 'strongly unified', meaning that the same formula works for any
|
||||
* input and output points, without needing special cases for the
|
||||
* identity or for doubling.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Create and destroy Edwards curve data structures.
|
||||
*
|
||||
* Similarly to ecc_weierstrass_curve, you don't have to provide
|
||||
* nonsquare_mod_p if you don't need ecc_edwards_point_new_from_y.
|
||||
*/
|
||||
EdwardsCurve *ecc_edwards_curve(
|
||||
mp_int *p, mp_int *d, mp_int *a, mp_int *nonsquare_mod_p);
|
||||
void ecc_edwards_curve_free(EdwardsCurve *);
|
||||
|
||||
/*
|
||||
* Create points.
|
||||
*
|
||||
* There's no need to have a separate function to create the identity
|
||||
* point, because you can just pass x=0 and y=1 to the usual function.
|
||||
*
|
||||
* Similarly to the Weierstrass curve, ecc_edwards_point_new_from_y
|
||||
* creates a point given only its y-coordinate and the desired parity
|
||||
* of its x-coordinate, and you can only call it if you provided the
|
||||
* optional nonsquare_mod_p argument when creating the curve.
|
||||
*/
|
||||
EdwardsPoint *ecc_edwards_point_new(
|
||||
EdwardsCurve *curve, mp_int *x, mp_int *y);
|
||||
EdwardsPoint *ecc_edwards_point_new_from_y(
|
||||
EdwardsCurve *curve, mp_int *y, unsigned desired_x_parity);
|
||||
|
||||
/* Copy and free points. */
|
||||
void ecc_edwards_point_copy_into(EdwardsPoint *dest, EdwardsPoint *src);
|
||||
EdwardsPoint *ecc_edwards_point_copy(EdwardsPoint *ec);
|
||||
void ecc_edwards_point_free(EdwardsPoint *point);
|
||||
|
||||
/*
|
||||
* Arithmetic: add two points, and calculate an integer multiple of a
|
||||
* point.
|
||||
*/
|
||||
EdwardsPoint *ecc_edwards_add(EdwardsPoint *, EdwardsPoint *);
|
||||
EdwardsPoint *ecc_edwards_multiply(EdwardsPoint *, mp_int *);
|
||||
|
||||
/*
|
||||
* Query functions: compare two points for equality, and return the
|
||||
* affine coordinates of a point.
|
||||
*/
|
||||
unsigned ecc_edwards_eq(EdwardsPoint *, EdwardsPoint *);
|
||||
void ecc_edwards_get_affine(EdwardsPoint *wp, mp_int **x, mp_int **y);
|
||||
|
||||
#endif /* PUTTY_ECC_H */
|
||||
@@ -0,0 +1 @@
|
||||
/* Empty file touched by automake makefile to force rebuild of version.o */
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* A dummy Socket implementation which just holds an error message.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "tree234.h"
|
||||
#include "putty.h"
|
||||
#include "network.h"
|
||||
|
||||
typedef struct {
|
||||
char *error;
|
||||
Plug *plug;
|
||||
|
||||
Socket sock;
|
||||
} ErrorSocket;
|
||||
|
||||
static Plug *sk_error_plug(Socket *s, Plug *p)
|
||||
{
|
||||
ErrorSocket *es = container_of(s, ErrorSocket, sock);
|
||||
Plug *ret = es->plug;
|
||||
if (p)
|
||||
es->plug = p;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sk_error_close(Socket *s)
|
||||
{
|
||||
ErrorSocket *es = container_of(s, ErrorSocket, sock);
|
||||
|
||||
sfree(es->error);
|
||||
sfree(es);
|
||||
}
|
||||
|
||||
static const char *sk_error_socket_error(Socket *s)
|
||||
{
|
||||
ErrorSocket *es = container_of(s, ErrorSocket, sock);
|
||||
return es->error;
|
||||
}
|
||||
|
||||
static SocketPeerInfo *sk_error_peer_info(Socket *s)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const SocketVtable ErrorSocket_sockvt = {
|
||||
.plug = sk_error_plug,
|
||||
.close = sk_error_close,
|
||||
.socket_error = sk_error_socket_error,
|
||||
.peer_info = sk_error_peer_info,
|
||||
/* other methods are NULL */
|
||||
};
|
||||
|
||||
Socket *new_error_socket_consume_string(Plug *plug, char *errmsg)
|
||||
{
|
||||
ErrorSocket *es = snew(ErrorSocket);
|
||||
es->sock.vt = &ErrorSocket_sockvt;
|
||||
es->plug = plug;
|
||||
es->error = errmsg;
|
||||
return &es->sock;
|
||||
}
|
||||
|
||||
Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *msg;
|
||||
|
||||
va_start(ap, fmt);
|
||||
msg = dupvprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return new_error_socket_consume_string(plug, msg);
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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 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 void fuzz_palette_set(TermWin *tw, unsigned start, unsigned ncolours,
|
||||
const rgb *colours) {}
|
||||
static void fuzz_palette_get_overrides(TermWin *tw, Terminal *term) {}
|
||||
|
||||
static const TermWinVtable fuzz_termwin_vt = {
|
||||
.setup_draw_ctx = fuzz_setup_draw_ctx,
|
||||
.draw_text = fuzz_draw_text,
|
||||
.draw_cursor = fuzz_draw_cursor,
|
||||
.draw_trust_sigil = fuzz_draw_trust_sigil,
|
||||
.char_width = fuzz_char_width,
|
||||
.free_draw_ctx = fuzz_free_draw_ctx,
|
||||
.set_cursor_pos = fuzz_set_cursor_pos,
|
||||
.set_raw_mouse_mode = fuzz_set_raw_mouse_mode,
|
||||
.set_scrollbar = fuzz_set_scrollbar,
|
||||
.bell = fuzz_bell,
|
||||
.clip_write = fuzz_clip_write,
|
||||
.clip_request_paste = fuzz_clip_request_paste,
|
||||
.refresh = fuzz_refresh,
|
||||
.request_resize = fuzz_request_resize,
|
||||
.set_title = fuzz_set_title,
|
||||
.set_icon_title = fuzz_set_icon_title,
|
||||
.set_minimised = fuzz_set_minimised,
|
||||
.set_maximised = fuzz_set_maximised,
|
||||
.move = fuzz_move,
|
||||
.set_zorder = fuzz_set_zorder,
|
||||
.palette_set = fuzz_palette_set,
|
||||
.palette_get_overrides = fuzz_palette_get_overrides,
|
||||
};
|
||||
|
||||
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
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* ldisc.c: PuTTY line discipline. Sits between the input coming
|
||||
* from keypresses in the window, and the output channel leading to
|
||||
* the back end. Implements echo and/or local line editing,
|
||||
* depending on what's currently configured.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "terminal.h"
|
||||
#include "ldisc.h"
|
||||
|
||||
#define ECHOING (ldisc->localecho == FORCE_ON || \
|
||||
(ldisc->localecho == AUTO && \
|
||||
(backend_ldisc_option_state(ldisc->backend, LD_ECHO))))
|
||||
#define EDITING (ldisc->localedit == FORCE_ON || \
|
||||
(ldisc->localedit == AUTO && \
|
||||
(backend_ldisc_option_state(ldisc->backend, LD_EDIT))))
|
||||
|
||||
/* rutty: special entry point for local data (in windows.c) */
|
||||
#ifdef MOD_RUTTY
|
||||
int GetPuttyFlag(void) ;
|
||||
int GetRuttyFlag(void) ;
|
||||
|
||||
size_t win_seat_output_local(Seat *seat, bool is_stderr, const void *data, size_t len) ;
|
||||
|
||||
static size_t seat_stdout_local(void *frontend, const char *data, size_t len) {
|
||||
return win_seat_output_local(frontend,false,(const void *)data,len);
|
||||
}
|
||||
void c_write(Ldisc *ldisc, const void *buf, int len)
|
||||
{
|
||||
if( !GetPuttyFlag() && (GetRuttyFlag()>0) ) {
|
||||
//from_backend_local(ldisc->frontend, 0, buf, len);
|
||||
seat_stdout_local(ldisc->seat, buf, len);
|
||||
} else {
|
||||
seat_stdout(ldisc->seat, buf, len);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void c_write(Ldisc *ldisc, const void *buf, int len)
|
||||
{
|
||||
seat_stdout(ldisc->seat, buf, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int plen(Ldisc *ldisc, unsigned char c)
|
||||
{
|
||||
if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term)))
|
||||
return 1;
|
||||
else if (c < 128)
|
||||
return 2; /* ^x for some x */
|
||||
else if (in_utf(ldisc->term) && c >= 0xC0)
|
||||
return 1; /* UTF-8 introducer character
|
||||
* (FIXME: combining / wide chars) */
|
||||
else if (in_utf(ldisc->term) && c >= 0x80 && c < 0xC0)
|
||||
return 0; /* UTF-8 followup character */
|
||||
else
|
||||
return 4; /* <XY> hex representation */
|
||||
}
|
||||
|
||||
static void pwrite(Ldisc *ldisc, unsigned char c)
|
||||
{
|
||||
if ((c >= 32 && c <= 126) ||
|
||||
(!in_utf(ldisc->term) && c >= 0xA0) ||
|
||||
(in_utf(ldisc->term) && c >= 0x80)) {
|
||||
c_write(ldisc, &c, 1);
|
||||
} else if (c < 128) {
|
||||
char cc[2];
|
||||
cc[1] = (c == 127 ? '?' : c + 0x40);
|
||||
cc[0] = '^';
|
||||
c_write(ldisc, cc, 2);
|
||||
} else {
|
||||
char cc[5];
|
||||
sprintf(cc, "<%02X>", c);
|
||||
c_write(ldisc, cc, 4);
|
||||
}
|
||||
}
|
||||
|
||||
static bool char_start(Ldisc *ldisc, unsigned char c)
|
||||
{
|
||||
if (in_utf(ldisc->term))
|
||||
return (c < 0x80 || c >= 0xC0);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bsb(Ldisc *ldisc, int n)
|
||||
{
|
||||
while (n--)
|
||||
c_write(ldisc, "\010 \010", 3);
|
||||
}
|
||||
|
||||
#define CTRL(x) (x^'@')
|
||||
#define KCTRL(x) ((x^'@') | 0x100)
|
||||
|
||||
Ldisc *ldisc_create(Conf *conf, Terminal *term, Backend *backend, Seat *seat)
|
||||
{
|
||||
Ldisc *ldisc = snew(Ldisc);
|
||||
|
||||
ldisc->buf = NULL;
|
||||
ldisc->buflen = 0;
|
||||
ldisc->bufsiz = 0;
|
||||
ldisc->quotenext = false;
|
||||
|
||||
ldisc->backend = backend;
|
||||
ldisc->term = term;
|
||||
ldisc->seat = seat;
|
||||
|
||||
ldisc_configure(ldisc, conf);
|
||||
|
||||
/* Link ourselves into the backend and the terminal */
|
||||
if (term)
|
||||
term->ldisc = ldisc;
|
||||
if (backend)
|
||||
backend_provide_ldisc(backend, ldisc);
|
||||
|
||||
return ldisc;
|
||||
}
|
||||
|
||||
void ldisc_configure(Ldisc *ldisc, Conf *conf)
|
||||
{
|
||||
ldisc->telnet_keyboard = conf_get_bool(conf, CONF_telnet_keyboard);
|
||||
ldisc->telnet_newline = conf_get_bool(conf, CONF_telnet_newline);
|
||||
ldisc->protocol = conf_get_int(conf, CONF_protocol);
|
||||
ldisc->localecho = conf_get_int(conf, CONF_localecho);
|
||||
ldisc->localedit = conf_get_int(conf, CONF_localedit);
|
||||
}
|
||||
|
||||
void ldisc_free(Ldisc *ldisc)
|
||||
{
|
||||
if (ldisc->term)
|
||||
ldisc->term->ldisc = NULL;
|
||||
if (ldisc->backend)
|
||||
backend_provide_ldisc(ldisc->backend, NULL);
|
||||
if (ldisc->buf)
|
||||
sfree(ldisc->buf);
|
||||
sfree(ldisc);
|
||||
}
|
||||
|
||||
/* rutty: */
|
||||
#ifdef MOD_RUTTY
|
||||
#include "script.h"
|
||||
extern ScriptData scriptdata; /* in window.c */
|
||||
#endif /* rutty */
|
||||
|
||||
void ldisc_echoedit_update(Ldisc *ldisc)
|
||||
{
|
||||
seat_echoedit_update(ldisc->seat, ECHOING, EDITING);
|
||||
}
|
||||
|
||||
void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)
|
||||
{
|
||||
const char *buf = (const char *)vbuf;
|
||||
int keyflag = 0;
|
||||
|
||||
assert(ldisc->term);
|
||||
|
||||
/* rutty: */
|
||||
#ifdef MOD_RUTTY
|
||||
if( !GetPuttyFlag() && (GetRuttyFlag()>0) ) { script_local(&scriptdata, buf,len); }
|
||||
#endif /* rutty */
|
||||
|
||||
if (interactive) {
|
||||
/*
|
||||
* Interrupt a paste from the clipboard, if one was in
|
||||
* progress when the user pressed a key. This is easier than
|
||||
* buffering the current piece of data and saving it until the
|
||||
* terminal has finished pasting, and has the potential side
|
||||
* benefit of permitting a user to cancel an accidental huge
|
||||
* paste.
|
||||
*/
|
||||
term_nopaste(ldisc->term);
|
||||
}
|
||||
|
||||
/*
|
||||
* Less than zero means null terminated special string.
|
||||
*/
|
||||
if (len < 0) {
|
||||
len = strlen(buf);
|
||||
keyflag = KCTRL('@');
|
||||
}
|
||||
/*
|
||||
* Either perform local editing, or just send characters.
|
||||
*/
|
||||
if (EDITING) {
|
||||
while (len--) {
|
||||
int c;
|
||||
c = (unsigned char)(*buf++) + keyflag;
|
||||
if (!interactive && c == '\r')
|
||||
c += KCTRL('@');
|
||||
switch (ldisc->quotenext ? ' ' : c) {
|
||||
/*
|
||||
* ^h/^?: delete, and output BSBs, to return to
|
||||
* last character boundary (in UTF-8 mode this may
|
||||
* be more than one byte)
|
||||
* ^w: delete, and output BSBs, to return to last
|
||||
* space/nonspace boundary
|
||||
* ^u: delete, and output BSBs, to return to BOL
|
||||
* ^c: Do a ^u then send a telnet IP
|
||||
* ^z: Do a ^u then send a telnet SUSP
|
||||
* ^\: Do a ^u then send a telnet ABORT
|
||||
* ^r: echo "^R\n" and redraw line
|
||||
* ^v: quote next char
|
||||
* ^d: if at BOL, end of file and close connection,
|
||||
* else send line and reset to BOL
|
||||
* ^m: send line-plus-\r\n and reset to BOL
|
||||
*/
|
||||
case KCTRL('H'):
|
||||
case KCTRL('?'): /* backspace/delete */
|
||||
if (ldisc->buflen > 0) {
|
||||
do {
|
||||
if (ECHOING)
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
} while (!char_start(ldisc, ldisc->buf[ldisc->buflen]));
|
||||
}
|
||||
break;
|
||||
case CTRL('W'): /* delete word */
|
||||
while (ldisc->buflen > 0) {
|
||||
if (ECHOING)
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
if (ldisc->buflen > 0 &&
|
||||
isspace((unsigned char)ldisc->buf[ldisc->buflen-1]) &&
|
||||
!isspace((unsigned char)ldisc->buf[ldisc->buflen]))
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CTRL('U'): /* delete line */
|
||||
case CTRL('C'): /* Send IP */
|
||||
case CTRL('\\'): /* Quit */
|
||||
case CTRL('Z'): /* Suspend */
|
||||
while (ldisc->buflen > 0) {
|
||||
if (ECHOING)
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
}
|
||||
backend_special(ldisc->backend, SS_EL, 0);
|
||||
/*
|
||||
* We don't send IP, SUSP or ABORT if the user has
|
||||
* configured telnet specials off! This breaks
|
||||
* talkers otherwise.
|
||||
*/
|
||||
if (!ldisc->telnet_keyboard)
|
||||
goto default_case;
|
||||
if (c == CTRL('C'))
|
||||
backend_special(ldisc->backend, SS_IP, 0);
|
||||
if (c == CTRL('Z'))
|
||||
backend_special(ldisc->backend, SS_SUSP, 0);
|
||||
if (c == CTRL('\\'))
|
||||
backend_special(ldisc->backend, SS_ABORT, 0);
|
||||
break;
|
||||
case CTRL('R'): /* redraw line */
|
||||
if (ECHOING) {
|
||||
int i;
|
||||
c_write(ldisc, "^R\r\n", 4);
|
||||
for (i = 0; i < ldisc->buflen; i++)
|
||||
pwrite(ldisc, ldisc->buf[i]);
|
||||
}
|
||||
break;
|
||||
case CTRL('V'): /* quote next char */
|
||||
ldisc->quotenext = true;
|
||||
break;
|
||||
case CTRL('D'): /* logout or send */
|
||||
if (ldisc->buflen == 0) {
|
||||
backend_special(ldisc->backend, SS_EOF, 0);
|
||||
} else {
|
||||
backend_send(ldisc->backend, ldisc->buf, ldisc->buflen);
|
||||
ldisc->buflen = 0;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
* This particularly hideous bit of code from RDB
|
||||
* allows ordinary ^M^J to do the same thing as
|
||||
* magic-^M when in Raw protocol. The line `case
|
||||
* KCTRL('M'):' is _inside_ the if block. Thus:
|
||||
*
|
||||
* - receiving regular ^M goes straight to the
|
||||
* default clause and inserts as a literal ^M.
|
||||
* - receiving regular ^J _not_ directly after a
|
||||
* literal ^M (or not in Raw protocol) fails the
|
||||
* if condition, leaps to the bottom of the if,
|
||||
* and falls through into the default clause
|
||||
* again.
|
||||
* - receiving regular ^J just after a literal ^M
|
||||
* in Raw protocol passes the if condition,
|
||||
* deletes the literal ^M, and falls through
|
||||
* into the magic-^M code
|
||||
* - receiving a magic-^M empties the line buffer,
|
||||
* signals end-of-line in one of the various
|
||||
* entertaining ways, and _doesn't_ fall out of
|
||||
* the bottom of the if and through to the
|
||||
* default clause because of the break.
|
||||
*/
|
||||
case CTRL('J'):
|
||||
if (ldisc->protocol == PROT_RAW &&
|
||||
ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') {
|
||||
if (ECHOING)
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
/* FALLTHROUGH */
|
||||
case KCTRL('M'): /* send with newline */
|
||||
if (ldisc->buflen > 0)
|
||||
backend_send(ldisc->backend,
|
||||
ldisc->buf, ldisc->buflen);
|
||||
if (ldisc->protocol == PROT_RAW)
|
||||
backend_send(ldisc->backend, "\r\n", 2);
|
||||
else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
|
||||
backend_special(ldisc->backend, SS_EOL, 0);
|
||||
else
|
||||
backend_send(ldisc->backend, "\r", 1);
|
||||
if (ECHOING)
|
||||
c_write(ldisc, "\r\n", 2);
|
||||
ldisc->buflen = 0;
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
default: /* get to this label from ^V handler */
|
||||
default_case:
|
||||
sgrowarray(ldisc->buf, ldisc->bufsiz, ldisc->buflen);
|
||||
ldisc->buf[ldisc->buflen++] = c;
|
||||
if (ECHOING)
|
||||
pwrite(ldisc, (unsigned char) c);
|
||||
ldisc->quotenext = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ldisc->buflen != 0) {
|
||||
backend_send(ldisc->backend, ldisc->buf, ldisc->buflen);
|
||||
while (ldisc->buflen > 0) {
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
}
|
||||
}
|
||||
if (len > 0) {
|
||||
if (ECHOING)
|
||||
c_write(ldisc, buf, len);
|
||||
if (keyflag && ldisc->protocol == PROT_TELNET && len == 1) {
|
||||
switch (buf[0]) {
|
||||
case CTRL('M'):
|
||||
if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
|
||||
backend_special(ldisc->backend, SS_EOL, 0);
|
||||
else
|
||||
backend_send(ldisc->backend, "\r", 1);
|
||||
break;
|
||||
case CTRL('?'):
|
||||
case CTRL('H'):
|
||||
if (ldisc->telnet_keyboard) {
|
||||
backend_special(ldisc->backend, SS_EC, 0);
|
||||
break;
|
||||
}
|
||||
case CTRL('C'):
|
||||
if (ldisc->telnet_keyboard) {
|
||||
backend_special(ldisc->backend, SS_IP, 0);
|
||||
break;
|
||||
}
|
||||
case CTRL('Z'):
|
||||
if (ldisc->telnet_keyboard) {
|
||||
backend_special(ldisc->backend, SS_SUSP, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
backend_send(ldisc->backend, buf, len);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
backend_send(ldisc->backend, buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* ldisc.h: defines the Ldisc data structure used by ldisc.c and
|
||||
* ldiscucs.c. (Unfortunately it was necessary to split the ldisc
|
||||
* module in two, to avoid unnecessarily linking in the Unicode
|
||||
* stuff in tools that don't require it.)
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_LDISC_H
|
||||
#define PUTTY_LDISC_H
|
||||
|
||||
struct Ldisc_tag {
|
||||
Terminal *term;
|
||||
Backend *backend;
|
||||
Seat *seat;
|
||||
|
||||
/*
|
||||
* Values cached out of conf.
|
||||
*/
|
||||
bool telnet_keyboard, telnet_newline;
|
||||
int protocol, localecho, localedit;
|
||||
|
||||
char *buf;
|
||||
size_t buflen, bufsiz;
|
||||
bool quotenext;
|
||||
};
|
||||
|
||||
#endif /* PUTTY_LDISC_H */
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* licence.h - macro definitions for the PuTTY licence.
|
||||
*
|
||||
* Generated by licence.pl from LICENCE.
|
||||
* You should edit those files rather than editing this one.
|
||||
*/
|
||||
|
||||
#define LICENCE_TEXT(parsep) \
|
||||
"PuTTY is copyright 1997-2022 Simon Tatham." \
|
||||
parsep \
|
||||
"Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, and CORE SDI S.A." \
|
||||
parsep \
|
||||
"Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:" \
|
||||
parsep \
|
||||
"The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software." \
|
||||
parsep \
|
||||
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
|
||||
|
||||
#define SHORT_COPYRIGHT_DETAILS "1997-2022 Simon Tatham"
|
||||
@@ -0,0 +1,664 @@
|
||||
/*
|
||||
* Session logging.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
/* log session to file stuff ... */
|
||||
struct LogContext {
|
||||
FILE *lgfp;
|
||||
enum { L_CLOSED, L_OPENING, L_OPEN, L_ERROR } state;
|
||||
bufchain queue;
|
||||
Filename *currlogfilename;
|
||||
LogPolicy *lp;
|
||||
Conf *conf;
|
||||
int logtype; /* cached out of conf */
|
||||
};
|
||||
|
||||
static Filename *xlatlognam(Filename *s, char *hostname, int port,
|
||||
struct tm *tm);
|
||||
|
||||
#ifdef MOD_PERSO
|
||||
int GetPuttyFlag(void) ;
|
||||
int mkdir(const char *path, int mode);
|
||||
int insert( char * ch, const char * c, const int ipos ) ;
|
||||
int del( char * ch, const int start, const int length ) ;
|
||||
int poss( const char * c, const char * ch ) ;
|
||||
int posi( const char * c, const char * ch, const int ipos ) ;
|
||||
static int LogMode = 1 ;
|
||||
int SwitchLogMode(void) { LogMode = abs( LogMode - 1 ) ; return LogMode ; }
|
||||
|
||||
// Test l'existance du répertoire, sinon le créé
|
||||
void test_dir( Filename *filename ) {
|
||||
int i ; char * name ;
|
||||
if( filename == NULL ) return ;
|
||||
if( strlen( filename_to_str(filename) ) == 0 ) return ;
|
||||
|
||||
if( ( name = (char*) malloc( strlen( filename_to_str(filename) ) + 1 ) ) != NULL ) {
|
||||
strcpy( name, filename_to_str(filename) ) ;
|
||||
for( i=strlen(name)-1; i>=0 ; i-- )
|
||||
{ if( (name[i] == '\\') || (name[i]=='/' ) ) break ; }
|
||||
if( i > 0 ) {
|
||||
name[i] = '\0';
|
||||
mkdir( name, 777 ) ;
|
||||
}
|
||||
free( name ) ;
|
||||
}
|
||||
}
|
||||
|
||||
/* Procedure perso de conversion date (time_t) -> char* */
|
||||
size_t m_strftime( char *s, size_t max, const char *format, const struct tm *tm) {
|
||||
char * nfor, b[128] ;
|
||||
int p, i = 1 ;
|
||||
size_t res ;
|
||||
|
||||
if( (nfor = (char*) malloc( strlen( format ) + 1024 )) == NULL ) return 0 ;
|
||||
strcpy( nfor, format ) ;
|
||||
|
||||
sprintf( b, "%lu", mktime((struct tm*)tm) ) ;
|
||||
while( (p=posi( "%s", nfor, i )) > 0 ) {
|
||||
if( p==1 ) { del(nfor,p,2) ; insert(nfor,b,p); }
|
||||
else if( nfor[p-2] != '%' ) { del(nfor,p,2) ; insert(nfor,b,p); }
|
||||
i = p + 1 ;
|
||||
}
|
||||
while( (p=posi( "%t", nfor, i )) > 0 ) {
|
||||
if( p==1 ) { del(nfor,p,2) ; insert(nfor," ",p); }
|
||||
else if( nfor[p-2] != '%' ) { del(nfor,p,2) ; insert(nfor," ",p); }
|
||||
i = p + 1 ;
|
||||
}
|
||||
|
||||
res = strftime( s, max, nfor, tm ) ;
|
||||
|
||||
free( nfor ) ;
|
||||
return res ;
|
||||
}
|
||||
|
||||
/* Procedure perso de conversion date (struct _SYSTEMTIME) -> char* */
|
||||
size_t t_strftime( char *s, size_t max, const char *format, const struct tm tm, const SYSTEMTIME st ) {
|
||||
char * nfor, b[128] ;
|
||||
int p, i = 1 ;
|
||||
//struct tm tm ;
|
||||
size_t res = 0 ;
|
||||
/*
|
||||
tm.tm_sec = st.wSecond ;
|
||||
tm.tm_min = st.wMinute ;
|
||||
tm.tm_hour = st.wHour ;
|
||||
tm.tm_mday = st.wDay ;
|
||||
tm.tm_mon = st.wMonth - 1 ;
|
||||
tm.tm_year = st.wYear - 1900 ;
|
||||
tm.tm_wday = st.wDayOfWeek - 1 ;
|
||||
tm.tm_yday = 0 ;
|
||||
tm.tm_isdst = 0 ;
|
||||
*/
|
||||
if( (nfor = (char*) malloc( strlen( format ) + 1024 )) == NULL ) return 0 ;
|
||||
strcpy( nfor, format ) ;
|
||||
|
||||
sprintf( b, "%03u", st.wMilliseconds ) ;
|
||||
while( (p=posi( "%f", nfor, i )) > 0 ) {
|
||||
if( p==1 ) { del(nfor,p,2) ; insert(nfor,b,p); }
|
||||
else if( nfor[p-2] != '%' ) { del(nfor,p,2) ; insert(nfor,b,p); }
|
||||
i = p + 1 ;
|
||||
}
|
||||
|
||||
res = m_strftime( s, max, nfor, &tm ) ;
|
||||
|
||||
free( nfor ) ;
|
||||
|
||||
return res ;
|
||||
}
|
||||
|
||||
int log_writetimestamp( struct LogContext *ctx ) {
|
||||
// "%m/%d/%Y %H:%M:%S "
|
||||
if( strlen(conf_get_str(ctx->conf,CONF_logtimestamp) )==0 ) return 1 ;
|
||||
char buf[128] = "" ;
|
||||
|
||||
if( poss( "%f", conf_get_str(ctx->conf,CONF_logtimestamp) ) ) {
|
||||
SYSTEMTIME sysTime ;
|
||||
GetLocalTime( &sysTime ) ;
|
||||
time_t temps = time( 0 ) ;
|
||||
struct tm tm = * localtime( &temps ) ;
|
||||
t_strftime( buf, 127, conf_get_str(ctx->conf,CONF_logtimestamp), tm, sysTime ) ;
|
||||
}
|
||||
else {
|
||||
time_t temps = time( 0 ) ;
|
||||
struct tm tm = * localtime( &temps ) ;
|
||||
m_strftime( buf, 127, conf_get_str(ctx->conf,CONF_logtimestamp), &tm ) ;
|
||||
}
|
||||
|
||||
fwrite(buf, 1, strlen(buf), ctx->lgfp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int timestamp_newline = 1 ;
|
||||
static int timestamp_newfile = 0 ;
|
||||
|
||||
void timestamp_change_filename( void ) { timestamp_newfile = 1 ; timestamp_newline = 1 ; }
|
||||
static void logfopen_callback(void *handle, int mode) ;
|
||||
void logfile_reinit(void *handle) { if(handle!=NULL) { struct LogContext *ctx = (struct LogContext *)handle; logfclose(ctx) ; logfopen_callback(ctx,2) ; } }
|
||||
#endif
|
||||
/*
|
||||
* Internal wrapper function which must be called for _all_ output
|
||||
* to the log file. It takes care of opening the log file if it
|
||||
* isn't open, buffering data if it's in the process of being
|
||||
* opened asynchronously, etc.
|
||||
*/
|
||||
static void logwrite(LogContext *ctx, ptrlen data)
|
||||
{
|
||||
/*
|
||||
* In state L_CLOSED, we call logfopen, which will set the state
|
||||
* to one of L_OPENING, L_OPEN or L_ERROR. Hence we process all of
|
||||
* those three _after_ processing L_CLOSED.
|
||||
*/
|
||||
#ifdef MOD_PERSO
|
||||
if( !LogMode ) return ;
|
||||
if( timestamp_newfile ) {
|
||||
if (ctx->state == L_OPEN) { logfclose(ctx);}
|
||||
timestamp_newfile = 0 ;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ctx->state == L_CLOSED)
|
||||
logfopen(ctx);
|
||||
|
||||
if (ctx->state == L_OPENING) {
|
||||
bufchain_add(&ctx->queue, data.ptr, data.len);
|
||||
} else if (ctx->state == L_OPEN) {
|
||||
assert(ctx->lgfp);
|
||||
#ifdef MOD_PERSO
|
||||
if( !GetPuttyFlag() ) {
|
||||
if( timestamp_newline ) { log_writetimestamp( ctx ) ; timestamp_newline = 0 ; }
|
||||
char * c = (char*)(data.ptr+data.len-1) ;
|
||||
if( c[0]=='\n' ) timestamp_newline = 1 ;
|
||||
}
|
||||
#endif
|
||||
if (fwrite(data.ptr, 1, data.len, ctx->lgfp) < data.len) {
|
||||
logfclose(ctx);
|
||||
ctx->state = L_ERROR;
|
||||
lp_eventlog(ctx->lp, "Disabled writing session log "
|
||||
"due to error while writing");
|
||||
}
|
||||
} /* else L_ERROR, so ignore the write */
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience wrapper on logwrite() which printf-formats the
|
||||
* string.
|
||||
*/
|
||||
static PRINTF_LIKE(2, 3) void logprintf(LogContext *ctx, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *data;
|
||||
|
||||
va_start(ap, fmt);
|
||||
data = dupvprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
logwrite(ctx, ptrlen_from_asciz(data));
|
||||
sfree(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush any open log file.
|
||||
*/
|
||||
void logflush(LogContext *ctx)
|
||||
{
|
||||
if (ctx->logtype > 0)
|
||||
if (ctx->state == L_OPEN)
|
||||
fflush(ctx->lgfp);
|
||||
}
|
||||
|
||||
static void logfopen_callback(void *vctx, int mode)
|
||||
{
|
||||
LogContext *ctx = (LogContext *)vctx;
|
||||
char buf[256], *event;
|
||||
struct tm tm;
|
||||
const char *fmode;
|
||||
bool shout = false;
|
||||
|
||||
if (mode == 0) {
|
||||
ctx->state = L_ERROR; /* disable logging */
|
||||
} else {
|
||||
fmode = (mode == 1 ? "ab" : "wb");
|
||||
ctx->lgfp = f_open(ctx->currlogfilename, fmode, false);
|
||||
if (ctx->lgfp) {
|
||||
ctx->state = L_OPEN;
|
||||
} else {
|
||||
ctx->state = L_ERROR;
|
||||
shout = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->state == L_OPEN && conf_get_bool(ctx->conf, CONF_logheader)) {
|
||||
/* Write header line into log file. */
|
||||
tm = ltime();
|
||||
strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
|
||||
logprintf(ctx, "=~=~=~=~=~=~=~=~=~=~=~= PuTTY log %s"
|
||||
" =~=~=~=~=~=~=~=~=~=~=~=\r\n", buf);
|
||||
}
|
||||
|
||||
event = dupprintf("%s session log (%s mode) to file: %s",
|
||||
ctx->state == L_ERROR ?
|
||||
(mode == 0 ? "Disabled writing" : "Error writing") :
|
||||
(mode == 1 ? "Appending" : "Writing new"),
|
||||
(ctx->logtype == LGTYP_ASCII ? "ASCII" :
|
||||
ctx->logtype == LGTYP_DEBUG ? "raw" :
|
||||
ctx->logtype == LGTYP_PACKETS ? "SSH packets" :
|
||||
ctx->logtype == LGTYP_SSHRAW ? "SSH raw data" :
|
||||
"unknown"),
|
||||
filename_to_str(ctx->currlogfilename));
|
||||
lp_eventlog(ctx->lp, event);
|
||||
if (shout) {
|
||||
/*
|
||||
* If we failed to open the log file due to filesystem error
|
||||
* (as opposed to user action such as clicking Cancel in the
|
||||
* askappend box), we should log it more prominently.
|
||||
*/
|
||||
lp_logging_error(ctx->lp, event);
|
||||
}
|
||||
sfree(event);
|
||||
|
||||
/*
|
||||
* Having either succeeded or failed in opening the log file,
|
||||
* we should write any queued data out.
|
||||
*/
|
||||
assert(ctx->state != L_OPENING); /* make _sure_ it won't be requeued */
|
||||
while (bufchain_size(&ctx->queue)) {
|
||||
ptrlen data = bufchain_prefix(&ctx->queue);
|
||||
logwrite(ctx, data);
|
||||
bufchain_consume(&ctx->queue, data.len);
|
||||
}
|
||||
logflush(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the log file. Takes care of detecting an already-existing
|
||||
* file and asking the user whether they want to append, overwrite
|
||||
* or cancel logging.
|
||||
*/
|
||||
void logfopen(LogContext *ctx)
|
||||
{
|
||||
struct tm tm;
|
||||
int mode;
|
||||
|
||||
/* Prevent repeat calls */
|
||||
if (ctx->state != L_CLOSED)
|
||||
return;
|
||||
|
||||
if (!ctx->logtype)
|
||||
return;
|
||||
|
||||
tm = ltime();
|
||||
|
||||
/* substitute special codes in file name */
|
||||
if (ctx->currlogfilename)
|
||||
filename_free(ctx->currlogfilename);
|
||||
ctx->currlogfilename =
|
||||
xlatlognam(conf_get_filename(ctx->conf, CONF_logfilename),
|
||||
conf_get_str(ctx->conf, CONF_host),
|
||||
conf_get_int(ctx->conf, CONF_port), &tm);
|
||||
#ifdef MOD_PERSO
|
||||
test_dir( ctx->currlogfilename ) ;
|
||||
#endif
|
||||
|
||||
|
||||
if (open_for_write_would_lose_data(ctx->currlogfilename)) {
|
||||
int logxfovr = conf_get_int(ctx->conf, CONF_logxfovr);
|
||||
if (logxfovr != LGXF_ASK) {
|
||||
mode = ((logxfovr == LGXF_OVR) ? 2 : 1);
|
||||
} else
|
||||
mode = lp_askappend(ctx->lp, ctx->currlogfilename,
|
||||
logfopen_callback, ctx);
|
||||
} else
|
||||
mode = 2; /* create == overwrite */
|
||||
|
||||
if (mode < 0)
|
||||
ctx->state = L_OPENING;
|
||||
else
|
||||
logfopen_callback(ctx, mode); /* open the file */
|
||||
}
|
||||
|
||||
void logfclose(LogContext *ctx)
|
||||
{
|
||||
if (ctx->lgfp) {
|
||||
fclose(ctx->lgfp);
|
||||
ctx->lgfp = NULL;
|
||||
}
|
||||
ctx->state = L_CLOSED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Log session traffic.
|
||||
*/
|
||||
void logtraffic(LogContext *ctx, unsigned char c, int logmode)
|
||||
{
|
||||
if (ctx->logtype > 0) {
|
||||
if (ctx->logtype == logmode)
|
||||
logwrite(ctx, make_ptrlen(&c, 1));
|
||||
}
|
||||
}
|
||||
|
||||
static void logevent_internal(LogContext *ctx, const char *event)
|
||||
{
|
||||
if (ctx->logtype == LGTYP_PACKETS || ctx->logtype == LGTYP_SSHRAW) {
|
||||
logprintf(ctx, "Event Log: %s\r\n", event);
|
||||
logflush(ctx);
|
||||
}
|
||||
lp_eventlog(ctx->lp, event);
|
||||
}
|
||||
|
||||
void logevent(LogContext *ctx, const char *event)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Replace newlines in Event Log messages with spaces. (Sometimes
|
||||
* the same message string is reused for the Event Log and a GUI
|
||||
* dialog box; newlines are sometimes appropriate in the latter,
|
||||
* but never in the former.)
|
||||
*/
|
||||
if (strchr(event, '\n') || strchr(event, '\r')) {
|
||||
char *dup = dupstr(event);
|
||||
char *p = dup, *q = dup;
|
||||
while (*p) {
|
||||
if (*p == '\r' || *p == '\n') {
|
||||
do {
|
||||
p++;
|
||||
} while (*p == '\r' || *p == '\n');
|
||||
*q++ = ' ';
|
||||
} else {
|
||||
*q++ = *p++;
|
||||
}
|
||||
}
|
||||
*q = '\0';
|
||||
logevent_internal(ctx, dup);
|
||||
sfree(dup);
|
||||
} else {
|
||||
logevent_internal(ctx, event);
|
||||
}
|
||||
}
|
||||
|
||||
void logevent_and_free(LogContext *ctx, char *event)
|
||||
{
|
||||
logevent(ctx, event);
|
||||
sfree(event);
|
||||
}
|
||||
|
||||
void logeventvf(LogContext *ctx, const char *fmt, va_list ap)
|
||||
{
|
||||
logevent_and_free(ctx, dupvprintf(fmt, ap));
|
||||
}
|
||||
|
||||
void logeventf(LogContext *ctx, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
logeventvf(ctx, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Log an SSH packet.
|
||||
* If n_blanks != 0, blank or omit some parts.
|
||||
* Set of blanking areas must be in increasing order.
|
||||
*/
|
||||
void log_packet(LogContext *ctx, int direction, int type,
|
||||
const char *texttype, const void *data, size_t len,
|
||||
int n_blanks, const struct logblank_t *blanks,
|
||||
const unsigned long *seq,
|
||||
unsigned downstream_id, const char *additional_log_text)
|
||||
{
|
||||
char dumpdata[128], smalldata[5];
|
||||
size_t p = 0, b = 0, omitted = 0;
|
||||
int output_pos = 0; /* NZ if pending output in dumpdata */
|
||||
|
||||
if (!(ctx->logtype == LGTYP_SSHRAW ||
|
||||
(ctx->logtype == LGTYP_PACKETS && texttype)))
|
||||
return;
|
||||
|
||||
/* Packet header. */
|
||||
if (texttype) {
|
||||
logprintf(ctx, "%s packet ",
|
||||
direction == PKT_INCOMING ? "Incoming" : "Outgoing");
|
||||
|
||||
if (seq)
|
||||
logprintf(ctx, "#0x%lx, ", *seq);
|
||||
|
||||
logprintf(ctx, "type %d / 0x%02x (%s)", type, type, texttype);
|
||||
|
||||
if (downstream_id) {
|
||||
logprintf(ctx, " on behalf of downstream #%u", downstream_id);
|
||||
if (additional_log_text)
|
||||
logprintf(ctx, " (%s)", additional_log_text);
|
||||
}
|
||||
|
||||
logprintf(ctx, "\r\n");
|
||||
} else {
|
||||
/*
|
||||
* Raw data is logged with a timestamp, so that it's possible
|
||||
* to determine whether a mysterious delay occurred at the
|
||||
* client or server end. (Timestamping the raw data avoids
|
||||
* cluttering the normal case of only logging decrypted SSH
|
||||
* messages, and also adds conceptual rigour in the case where
|
||||
* an SSH message arrives in several pieces.)
|
||||
*/
|
||||
char buf[256];
|
||||
struct tm tm;
|
||||
tm = ltime();
|
||||
strftime(buf, 24, "%Y-%m-%d %H:%M:%S", &tm);
|
||||
logprintf(ctx, "%s raw data at %s\r\n",
|
||||
direction == PKT_INCOMING ? "Incoming" : "Outgoing",
|
||||
buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Output a hex/ASCII dump of the packet body, blanking/omitting
|
||||
* parts as specified.
|
||||
*/
|
||||
while (p < len) {
|
||||
int blktype;
|
||||
|
||||
/* Move to a current entry in the blanking array. */
|
||||
while ((b < n_blanks) &&
|
||||
(p >= blanks[b].offset + blanks[b].len))
|
||||
b++;
|
||||
/* Work out what type of blanking to apply to
|
||||
* this byte. */
|
||||
blktype = PKTLOG_EMIT; /* default */
|
||||
if ((b < n_blanks) &&
|
||||
(p >= blanks[b].offset) &&
|
||||
(p < blanks[b].offset + blanks[b].len))
|
||||
blktype = blanks[b].type;
|
||||
|
||||
/* If we're about to stop omitting, it's time to say how
|
||||
* much we omitted. */
|
||||
if ((blktype != PKTLOG_OMIT) && omitted) {
|
||||
logprintf(ctx, " (%"SIZEu" byte%s omitted)\r\n",
|
||||
omitted, (omitted==1?"":"s"));
|
||||
omitted = 0;
|
||||
}
|
||||
|
||||
/* (Re-)initialise dumpdata as necessary
|
||||
* (start of row, or if we've just stopped omitting) */
|
||||
if (!output_pos && !omitted)
|
||||
sprintf(dumpdata, " %08"SIZEx"%*s\r\n",
|
||||
p-(p%16), 1+3*16+2+16, "");
|
||||
|
||||
/* Deal with the current byte. */
|
||||
if (blktype == PKTLOG_OMIT) {
|
||||
omitted++;
|
||||
} else {
|
||||
int c;
|
||||
if (blktype == PKTLOG_BLANK) {
|
||||
c = 'X';
|
||||
sprintf(smalldata, "XX");
|
||||
} else { /* PKTLOG_EMIT */
|
||||
c = ((const unsigned char *)data)[p];
|
||||
sprintf(smalldata, "%02x", c);
|
||||
}
|
||||
dumpdata[10+2+3*(p%16)] = smalldata[0];
|
||||
dumpdata[10+2+3*(p%16)+1] = smalldata[1];
|
||||
dumpdata[10+1+3*16+2+(p%16)] = (c >= 0x20 && c < 0x7F ? c : '.');
|
||||
output_pos = (p%16) + 1;
|
||||
}
|
||||
|
||||
p++;
|
||||
|
||||
/* Flush row if necessary */
|
||||
if (((p % 16) == 0) || (p == len) || omitted) {
|
||||
if (output_pos) {
|
||||
strcpy(dumpdata + 10+1+3*16+2+output_pos, "\r\n");
|
||||
logwrite(ctx, ptrlen_from_asciz(dumpdata));
|
||||
output_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Tidy up */
|
||||
if (omitted)
|
||||
logprintf(ctx, " (%"SIZEu" byte%s omitted)\r\n",
|
||||
omitted, (omitted==1?"":"s"));
|
||||
logflush(ctx);
|
||||
}
|
||||
|
||||
LogContext *log_init(LogPolicy *lp, Conf *conf)
|
||||
{
|
||||
LogContext *ctx = snew(LogContext);
|
||||
ctx->lgfp = NULL;
|
||||
ctx->state = L_CLOSED;
|
||||
ctx->lp = lp;
|
||||
ctx->conf = conf_copy(conf);
|
||||
ctx->logtype = conf_get_int(ctx->conf, CONF_logtype);
|
||||
ctx->currlogfilename = NULL;
|
||||
bufchain_init(&ctx->queue);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void log_free(LogContext *ctx)
|
||||
{
|
||||
logfclose(ctx);
|
||||
bufchain_clear(&ctx->queue);
|
||||
if (ctx->currlogfilename)
|
||||
filename_free(ctx->currlogfilename);
|
||||
conf_free(ctx->conf);
|
||||
sfree(ctx);
|
||||
}
|
||||
|
||||
void log_reconfig(LogContext *ctx, Conf *conf)
|
||||
{
|
||||
bool reset_logging;
|
||||
|
||||
if (!filename_equal(conf_get_filename(ctx->conf, CONF_logfilename),
|
||||
conf_get_filename(conf, CONF_logfilename)) ||
|
||||
conf_get_int(ctx->conf, CONF_logtype) !=
|
||||
conf_get_int(conf, CONF_logtype))
|
||||
reset_logging = true;
|
||||
else
|
||||
reset_logging = false;
|
||||
|
||||
if (reset_logging)
|
||||
logfclose(ctx);
|
||||
|
||||
conf_free(ctx->conf);
|
||||
ctx->conf = conf_copy(conf);
|
||||
|
||||
ctx->logtype = conf_get_int(ctx->conf, CONF_logtype);
|
||||
|
||||
if (reset_logging)
|
||||
logfopen(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* translate format codes into time/date strings
|
||||
* and insert them into log file name
|
||||
*
|
||||
* "&Y":YYYY "&m":MM "&d":DD "&T":hhmmss "&h":<hostname> "&&":&
|
||||
*/
|
||||
static Filename *xlatlognam(Filename *src, char *hostname, int port,
|
||||
struct tm *tm)
|
||||
{
|
||||
#ifdef MOD_PERSO
|
||||
char buf[100], *bufp;
|
||||
#else
|
||||
char buf[32], *bufp;
|
||||
#endif
|
||||
int size;
|
||||
strbuf *buffer;
|
||||
const char *s;
|
||||
Filename *ret;
|
||||
|
||||
buffer = strbuf_new();
|
||||
s = filename_to_str(src);
|
||||
|
||||
while (*s) {
|
||||
bool sanitise = false;
|
||||
/* Let (bufp, len) be the string to append. */
|
||||
bufp = buf; /* don't usually override this */
|
||||
if (*s == '&') {
|
||||
char c;
|
||||
s++;
|
||||
size = 0;
|
||||
if (*s) switch (c = *s++, tolower((unsigned char)c)) {
|
||||
case 'y':
|
||||
size = strftime(buf, sizeof(buf), "%Y", tm);
|
||||
break;
|
||||
case 'm':
|
||||
size = strftime(buf, sizeof(buf), "%m", tm);
|
||||
break;
|
||||
case 'd':
|
||||
size = strftime(buf, sizeof(buf), "%d", tm);
|
||||
break;
|
||||
case 't':
|
||||
size = strftime(buf, sizeof(buf), "%H%M%S", tm);
|
||||
break;
|
||||
#ifdef MOD_PERSO
|
||||
case 'h':
|
||||
strcpy(buf,hostname) ;
|
||||
int i ; while( (i=poss(":",buf))>0 ) { buf[i-1]='-' ; } // Pour gerer IPv6
|
||||
bufp=buf ;
|
||||
#else
|
||||
case 'h':
|
||||
bufp = hostname;
|
||||
#endif
|
||||
size = strlen(bufp);
|
||||
break;
|
||||
case 'p':
|
||||
size = sprintf(buf, "%d", port);
|
||||
break;
|
||||
default:
|
||||
buf[0] = '&';
|
||||
size = 1;
|
||||
if (c != '&')
|
||||
buf[size++] = c;
|
||||
}
|
||||
/* Never allow path separators - or any other illegal
|
||||
* filename character - to come out of any of these
|
||||
* auto-format directives. E.g. 'hostname' can contain
|
||||
* colons, if it's an IPv6 address, and colons aren't
|
||||
* legal in filenames on Windows. */
|
||||
sanitise = true;
|
||||
} else {
|
||||
buf[0] = *s++;
|
||||
size = 1;
|
||||
}
|
||||
while (size-- > 0) {
|
||||
char c = *bufp++;
|
||||
if (sanitise)
|
||||
c = filename_char_sanitise(c);
|
||||
put_byte(buffer, c);
|
||||
}
|
||||
}
|
||||
|
||||
ret = filename_from_str(buffer->s);
|
||||
strbuf_free(buffer);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* SSH main session channel handling.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "sshppl.h"
|
||||
#include "sshchan.h"
|
||||
|
||||
static void mainchan_free(Channel *chan);
|
||||
static void mainchan_open_confirmation(Channel *chan);
|
||||
static void mainchan_open_failure(Channel *chan, const char *errtext);
|
||||
static size_t mainchan_send(
|
||||
Channel *chan, bool is_stderr, const void *, size_t);
|
||||
static void mainchan_send_eof(Channel *chan);
|
||||
static void mainchan_set_input_wanted(Channel *chan, bool wanted);
|
||||
static char *mainchan_log_close_msg(Channel *chan);
|
||||
static bool mainchan_rcvd_exit_status(Channel *chan, int status);
|
||||
static bool mainchan_rcvd_exit_signal(
|
||||
Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg);
|
||||
static bool mainchan_rcvd_exit_signal_numeric(
|
||||
Channel *chan, int signum, bool core_dumped, ptrlen msg);
|
||||
static void mainchan_request_response(Channel *chan, bool success);
|
||||
|
||||
static const ChannelVtable mainchan_channelvt = {
|
||||
.free = mainchan_free,
|
||||
.open_confirmation = mainchan_open_confirmation,
|
||||
.open_failed = mainchan_open_failure,
|
||||
.send = mainchan_send,
|
||||
.send_eof = mainchan_send_eof,
|
||||
.set_input_wanted = mainchan_set_input_wanted,
|
||||
.log_close_msg = mainchan_log_close_msg,
|
||||
.want_close = chan_default_want_close,
|
||||
.rcvd_exit_status = mainchan_rcvd_exit_status,
|
||||
.rcvd_exit_signal = mainchan_rcvd_exit_signal,
|
||||
.rcvd_exit_signal_numeric = mainchan_rcvd_exit_signal_numeric,
|
||||
.run_shell = chan_no_run_shell,
|
||||
.run_command = chan_no_run_command,
|
||||
.run_subsystem = chan_no_run_subsystem,
|
||||
.enable_x11_forwarding = chan_no_enable_x11_forwarding,
|
||||
.enable_agent_forwarding = chan_no_enable_agent_forwarding,
|
||||
.allocate_pty = chan_no_allocate_pty,
|
||||
.set_env = chan_no_set_env,
|
||||
.send_break = chan_no_send_break,
|
||||
.send_signal = chan_no_send_signal,
|
||||
.change_window_size = chan_no_change_window_size,
|
||||
.request_response = mainchan_request_response,
|
||||
};
|
||||
|
||||
typedef enum MainChanType {
|
||||
MAINCHAN_SESSION, MAINCHAN_DIRECT_TCPIP
|
||||
} MainChanType;
|
||||
|
||||
struct mainchan {
|
||||
SshChannel *sc;
|
||||
Conf *conf;
|
||||
PacketProtocolLayer *ppl;
|
||||
ConnectionLayer *cl;
|
||||
|
||||
MainChanType type;
|
||||
bool is_simple;
|
||||
|
||||
bool req_x11, req_agent, req_pty, req_cmd_primary, req_cmd_fallback;
|
||||
int n_req_env, n_env_replies, n_env_fails;
|
||||
bool eof_pending, eof_sent, got_pty, ready;
|
||||
|
||||
int term_width, term_height;
|
||||
|
||||
Channel chan;
|
||||
};
|
||||
|
||||
mainchan *mainchan_new(
|
||||
PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf,
|
||||
int term_width, int term_height, bool is_simple, SshChannel **sc_out)
|
||||
{
|
||||
mainchan *mc;
|
||||
|
||||
if (conf_get_bool(conf, CONF_ssh_no_shell))
|
||||
return NULL; /* no main channel at all */
|
||||
|
||||
mc = snew(mainchan);
|
||||
memset(mc, 0, sizeof(mainchan));
|
||||
mc->ppl = ppl;
|
||||
mc->cl = cl;
|
||||
mc->conf = conf_copy(conf);
|
||||
mc->term_width = term_width;
|
||||
mc->term_height = term_height;
|
||||
mc->is_simple = is_simple;
|
||||
|
||||
mc->sc = NULL;
|
||||
mc->chan.vt = &mainchan_channelvt;
|
||||
mc->chan.initial_fixed_window_size = 0;
|
||||
|
||||
if (*conf_get_str(mc->conf, CONF_ssh_nc_host)) {
|
||||
const char *host = conf_get_str(mc->conf, CONF_ssh_nc_host);
|
||||
int port = conf_get_int(mc->conf, CONF_ssh_nc_port);
|
||||
|
||||
mc->sc = ssh_lportfwd_open(cl, host, port, "main channel",
|
||||
NULL, &mc->chan);
|
||||
mc->type = MAINCHAN_DIRECT_TCPIP;
|
||||
} else {
|
||||
mc->sc = ssh_session_open(cl, &mc->chan);
|
||||
mc->type = MAINCHAN_SESSION;
|
||||
}
|
||||
|
||||
if (sc_out) *sc_out = mc->sc;
|
||||
return mc;
|
||||
}
|
||||
|
||||
static void mainchan_free(Channel *chan)
|
||||
{
|
||||
assert(chan->vt == &mainchan_channelvt);
|
||||
mainchan *mc = container_of(chan, mainchan, chan);
|
||||
conf_free(mc->conf);
|
||||
sfree(mc);
|
||||
}
|
||||
|
||||
static void mainchan_try_fallback_command(mainchan *mc);
|
||||
static void mainchan_ready(mainchan *mc);
|
||||
|
||||
static void mainchan_open_confirmation(Channel *chan)
|
||||
{
|
||||
mainchan *mc = container_of(chan, mainchan, chan);
|
||||
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||||
|
||||
seat_update_specials_menu(mc->ppl->seat);
|
||||
ppl_logevent("Opened main channel");
|
||||
|
||||
if (mc->is_simple)
|
||||
sshfwd_hint_channel_is_simple(mc->sc);
|
||||
|
||||
if (mc->type == MAINCHAN_SESSION) {
|
||||
/*
|
||||
* Send the CHANNEL_REQUESTS for the main session channel.
|
||||
*/
|
||||
char *key, *val, *cmd;
|
||||
struct X11Display *x11disp;
|
||||
struct X11FakeAuth *x11auth;
|
||||
bool retry_cmd_now = false;
|
||||
|
||||
if (conf_get_bool(mc->conf, CONF_x11_forward)) {
|
||||
char *x11_setup_err;
|
||||
if ((x11disp = x11_setup_display(
|
||||
conf_get_str(mc->conf, CONF_x11_display),
|
||||
mc->conf, &x11_setup_err)) == NULL) {
|
||||
ppl_logevent("X11 forwarding not enabled: unable to"
|
||||
" initialise X display: %s", x11_setup_err);
|
||||
sfree(x11_setup_err);
|
||||
} else {
|
||||
x11auth = ssh_add_x11_display(
|
||||
mc->cl, conf_get_int(mc->conf, CONF_x11_auth), x11disp);
|
||||
|
||||
sshfwd_request_x11_forwarding(
|
||||
mc->sc, true, x11auth->protoname, x11auth->datastring,
|
||||
x11disp->screennum, false);
|
||||
mc->req_x11 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssh_agent_forwarding_permitted(mc->cl)) {
|
||||
sshfwd_request_agent_forwarding(mc->sc, true);
|
||||
mc->req_agent = true;
|
||||
}
|
||||
|
||||
if (!conf_get_bool(mc->conf, CONF_nopty)) {
|
||||
sshfwd_request_pty(
|
||||
mc->sc, true, mc->conf, mc->term_width, mc->term_height);
|
||||
mc->req_pty = true;
|
||||
}
|
||||
|
||||
for (val = conf_get_str_strs(mc->conf, CONF_environmt, NULL, &key);
|
||||
val != NULL;
|
||||
val = conf_get_str_strs(mc->conf, CONF_environmt, key, &key)) {
|
||||
sshfwd_send_env_var(mc->sc, true, key, val);
|
||||
mc->n_req_env++;
|
||||
}
|
||||
if (mc->n_req_env)
|
||||
ppl_logevent("Sent %d environment variables", mc->n_req_env);
|
||||
|
||||
cmd = conf_get_str(mc->conf, CONF_remote_cmd);
|
||||
if (conf_get_bool(mc->conf, CONF_ssh_subsys)) {
|
||||
retry_cmd_now = !sshfwd_start_subsystem(mc->sc, true, cmd);
|
||||
} else if (*cmd) {
|
||||
sshfwd_start_command(mc->sc, true, cmd);
|
||||
} else {
|
||||
sshfwd_start_shell(mc->sc, true);
|
||||
}
|
||||
|
||||
if (retry_cmd_now)
|
||||
mainchan_try_fallback_command(mc);
|
||||
else
|
||||
mc->req_cmd_primary = true;
|
||||
|
||||
} else {
|
||||
ssh_set_ldisc_option(mc->cl, LD_ECHO, true);
|
||||
ssh_set_ldisc_option(mc->cl, LD_EDIT, true);
|
||||
mainchan_ready(mc);
|
||||
}
|
||||
}
|
||||
|
||||
static void mainchan_try_fallback_command(mainchan *mc)
|
||||
{
|
||||
const char *cmd = conf_get_str(mc->conf, CONF_remote_cmd2);
|
||||
if (conf_get_bool(mc->conf, CONF_ssh_subsys2)) {
|
||||
sshfwd_start_subsystem(mc->sc, true, cmd);
|
||||
} else {
|
||||
sshfwd_start_command(mc->sc, true, cmd);
|
||||
}
|
||||
mc->req_cmd_fallback = true;
|
||||
}
|
||||
|
||||
static void mainchan_request_response(Channel *chan, bool success)
|
||||
{
|
||||
assert(chan->vt == &mainchan_channelvt);
|
||||
mainchan *mc = container_of(chan, mainchan, chan);
|
||||
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||||
|
||||
if (mc->req_x11) {
|
||||
mc->req_x11 = false;
|
||||
|
||||
if (success) {
|
||||
ppl_logevent("X11 forwarding enabled");
|
||||
ssh_enable_x_fwd(mc->cl);
|
||||
} else {
|
||||
ppl_logevent("X11 forwarding refused");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mc->req_agent) {
|
||||
mc->req_agent = false;
|
||||
|
||||
if (success) {
|
||||
ppl_logevent("Agent forwarding enabled");
|
||||
} else {
|
||||
ppl_logevent("Agent forwarding refused");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mc->req_pty) {
|
||||
mc->req_pty = false;
|
||||
|
||||
if (success) {
|
||||
ppl_logevent("Allocated pty");
|
||||
mc->got_pty = true;
|
||||
} else {
|
||||
ppl_logevent("Server refused to allocate pty");
|
||||
ppl_printf("Server refused to allocate pty\r\n");
|
||||
ssh_set_ldisc_option(mc->cl, LD_ECHO, true);
|
||||
ssh_set_ldisc_option(mc->cl, LD_EDIT, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mc->n_env_replies < mc->n_req_env) {
|
||||
int j = mc->n_env_replies++;
|
||||
if (!success) {
|
||||
ppl_logevent("Server refused to set environment variable %s",
|
||||
conf_get_str_nthstrkey(mc->conf,
|
||||
CONF_environmt, j));
|
||||
mc->n_env_fails++;
|
||||
}
|
||||
|
||||
if (mc->n_env_replies == mc->n_req_env) {
|
||||
if (mc->n_env_fails == 0) {
|
||||
ppl_logevent("All environment variables successfully set");
|
||||
} else if (mc->n_env_fails == mc->n_req_env) {
|
||||
ppl_logevent("All environment variables refused");
|
||||
ppl_printf("Server refused to set environment "
|
||||
"variables\r\n");
|
||||
} else {
|
||||
ppl_printf("Server refused to set all environment "
|
||||
"variables\r\n");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mc->req_cmd_primary) {
|
||||
mc->req_cmd_primary = false;
|
||||
|
||||
if (success) {
|
||||
ppl_logevent("Started a shell/command");
|
||||
mainchan_ready(mc);
|
||||
} else if (*conf_get_str(mc->conf, CONF_remote_cmd2)) {
|
||||
ppl_logevent("Primary command failed; attempting fallback");
|
||||
mainchan_try_fallback_command(mc);
|
||||
} else {
|
||||
/*
|
||||
* If there's no remote_cmd2 configured, then we have no
|
||||
* fallback command, so we've run out of options.
|
||||
*/
|
||||
ssh_sw_abort(mc->ppl->ssh,
|
||||
"Server refused to start a shell/command");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mc->req_cmd_fallback) {
|
||||
mc->req_cmd_fallback = false;
|
||||
|
||||
if (success) {
|
||||
ppl_logevent("Started a shell/command");
|
||||
ssh_got_fallback_cmd(mc->ppl->ssh);
|
||||
mainchan_ready(mc);
|
||||
} else {
|
||||
ssh_sw_abort(mc->ppl->ssh,
|
||||
"Server refused to start a shell/command");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void mainchan_ready(mainchan *mc)
|
||||
{
|
||||
mc->ready = true;
|
||||
|
||||
ssh_set_wants_user_input(mc->cl, true);
|
||||
ssh_ppl_got_user_input(mc->ppl); /* in case any is already queued */
|
||||
|
||||
/* If an EOF arrived before we were ready, handle it now. */
|
||||
if (mc->eof_pending) {
|
||||
mc->eof_pending = false;
|
||||
mainchan_special_cmd(mc, SS_EOF, 0);
|
||||
}
|
||||
|
||||
ssh_ldisc_update(mc->ppl->ssh);
|
||||
queue_idempotent_callback(&mc->ppl->ic_process_queue);
|
||||
}
|
||||
|
||||
static void mainchan_open_failure(Channel *chan, const char *errtext)
|
||||
{
|
||||
assert(chan->vt == &mainchan_channelvt);
|
||||
mainchan *mc = container_of(chan, mainchan, chan);
|
||||
|
||||
ssh_sw_abort_deferred(mc->ppl->ssh,
|
||||
"Server refused to open main channel: %s", errtext);
|
||||
}
|
||||
|
||||
static size_t mainchan_send(Channel *chan, bool is_stderr,
|
||||
const void *data, size_t length)
|
||||
{
|
||||
assert(chan->vt == &mainchan_channelvt);
|
||||
mainchan *mc = container_of(chan, mainchan, chan);
|
||||
return seat_output(mc->ppl->seat, is_stderr, data, length);
|
||||
}
|
||||
|
||||
static void mainchan_send_eof(Channel *chan)
|
||||
{
|
||||
assert(chan->vt == &mainchan_channelvt);
|
||||
mainchan *mc = container_of(chan, mainchan, chan);
|
||||
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||||
|
||||
if (!mc->eof_sent && (seat_eof(mc->ppl->seat) || mc->got_pty)) {
|
||||
/*
|
||||
* Either seat_eof told us that the front end wants us to
|
||||
* close the outgoing side of the connection as soon as we see
|
||||
* EOF from the far end, or else we've unilaterally decided to
|
||||
* do that because we've allocated a remote pty and hence EOF
|
||||
* isn't a particularly meaningful concept.
|
||||
*/
|
||||
sshfwd_write_eof(mc->sc);
|
||||
ppl_logevent("Sent EOF message");
|
||||
mc->eof_sent = true;
|
||||
ssh_set_wants_user_input(mc->cl, false); /* stop reading from stdin */
|
||||
}
|
||||
}
|
||||
|
||||
static void mainchan_set_input_wanted(Channel *chan, bool wanted)
|
||||
{
|
||||
assert(chan->vt == &mainchan_channelvt);
|
||||
mainchan *mc = container_of(chan, mainchan, chan);
|
||||
|
||||
/*
|
||||
* This is the main channel of the SSH session, i.e. the one tied
|
||||
* to the standard input (or GUI) of the primary SSH client user
|
||||
* interface. So ssh->send_ok is how we control whether we're
|
||||
* reading from that input.
|
||||
*/
|
||||
ssh_set_wants_user_input(mc->cl, wanted);
|
||||
}
|
||||
|
||||
static char *mainchan_log_close_msg(Channel *chan)
|
||||
{
|
||||
return dupstr("Main session channel closed");
|
||||
}
|
||||
|
||||
static bool mainchan_rcvd_exit_status(Channel *chan, int status)
|
||||
{
|
||||
assert(chan->vt == &mainchan_channelvt);
|
||||
mainchan *mc = container_of(chan, mainchan, chan);
|
||||
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||||
|
||||
ssh_got_exitcode(mc->ppl->ssh, status);
|
||||
ppl_logevent("Session sent command exit status %d", status);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mainchan_log_exit_signal_common(
|
||||
mainchan *mc, const char *sigdesc,
|
||||
bool core_dumped, ptrlen msg)
|
||||
{
|
||||
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||||
|
||||
const char *core_msg = core_dumped ? " (core dumped)" : "";
|
||||
const char *msg_pre = (msg.len ? " (" : "");
|
||||
const char *msg_post = (msg.len ? ")" : "");
|
||||
ppl_logevent("Session exited on %s%s%s%.*s%s",
|
||||
sigdesc, core_msg, msg_pre, PTRLEN_PRINTF(msg), msg_post);
|
||||
}
|
||||
|
||||
static bool mainchan_rcvd_exit_signal(
|
||||
Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg)
|
||||
{
|
||||
assert(chan->vt == &mainchan_channelvt);
|
||||
mainchan *mc = container_of(chan, mainchan, chan);
|
||||
int exitcode;
|
||||
char *signame_str;
|
||||
|
||||
/*
|
||||
* Translate the signal description back into a locally meaningful
|
||||
* number, or 128 if the string didn't match any we recognise.
|
||||
*/
|
||||
exitcode = 128;
|
||||
|
||||
#define SIGNAL_SUB(s) \
|
||||
if (ptrlen_eq_string(signame, #s)) \
|
||||
exitcode = 128 + SIG ## s;
|
||||
#define SIGNAL_MAIN(s, text) SIGNAL_SUB(s)
|
||||
#define SIGNALS_LOCAL_ONLY
|
||||
#include "sshsignals.h"
|
||||
#undef SIGNAL_SUB
|
||||
#undef SIGNAL_MAIN
|
||||
#undef SIGNALS_LOCAL_ONLY
|
||||
|
||||
ssh_got_exitcode(mc->ppl->ssh, exitcode);
|
||||
if (exitcode == 128)
|
||||
signame_str = dupprintf("unrecognised signal \"%.*s\"",
|
||||
PTRLEN_PRINTF(signame));
|
||||
else
|
||||
signame_str = dupprintf("signal SIG%.*s", PTRLEN_PRINTF(signame));
|
||||
mainchan_log_exit_signal_common(mc, signame_str, core_dumped, msg);
|
||||
sfree(signame_str);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mainchan_rcvd_exit_signal_numeric(
|
||||
Channel *chan, int signum, bool core_dumped, ptrlen msg)
|
||||
{
|
||||
assert(chan->vt == &mainchan_channelvt);
|
||||
mainchan *mc = container_of(chan, mainchan, chan);
|
||||
char *signum_str;
|
||||
|
||||
ssh_got_exitcode(mc->ppl->ssh, 128 + signum);
|
||||
signum_str = dupprintf("signal %d", signum);
|
||||
mainchan_log_exit_signal_common(mc, signum_str, core_dumped, msg);
|
||||
sfree(signum_str);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mainchan_get_specials(
|
||||
mainchan *mc, add_special_fn_t add_special, void *ctx)
|
||||
{
|
||||
/* FIXME: this _does_ depend on whether these services are supported */
|
||||
|
||||
add_special(ctx, "Break", SS_BRK, 0);
|
||||
|
||||
#define SIGNAL_MAIN(name, desc) \
|
||||
add_special(ctx, "SIG" #name " (" desc ")", SS_SIG ## name, 0);
|
||||
#define SIGNAL_SUB(name)
|
||||
#include "sshsignals.h"
|
||||
#undef SIGNAL_MAIN
|
||||
#undef SIGNAL_SUB
|
||||
|
||||
add_special(ctx, "More signals", SS_SUBMENU, 0);
|
||||
|
||||
#define SIGNAL_MAIN(name, desc)
|
||||
#define SIGNAL_SUB(name) \
|
||||
add_special(ctx, "SIG" #name, SS_SIG ## name, 0);
|
||||
#include "sshsignals.h"
|
||||
#undef SIGNAL_MAIN
|
||||
#undef SIGNAL_SUB
|
||||
|
||||
add_special(ctx, NULL, SS_EXITMENU, 0);
|
||||
}
|
||||
|
||||
static const char *ssh_signal_lookup(SessionSpecialCode code)
|
||||
{
|
||||
#define SIGNAL_SUB(name) \
|
||||
if (code == SS_SIG ## name) return #name;
|
||||
#define SIGNAL_MAIN(name, desc) SIGNAL_SUB(name)
|
||||
#include "sshsignals.h"
|
||||
#undef SIGNAL_MAIN
|
||||
#undef SIGNAL_SUB
|
||||
|
||||
/* If none of those clauses matched, fail lookup. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg)
|
||||
{
|
||||
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||||
const char *signame;
|
||||
|
||||
if (code == SS_EOF) {
|
||||
if (!mc->ready) {
|
||||
/*
|
||||
* Buffer the EOF to send as soon as the main channel is
|
||||
* fully set up.
|
||||
*/
|
||||
mc->eof_pending = true;
|
||||
} else if (!mc->eof_sent) {
|
||||
sshfwd_write_eof(mc->sc);
|
||||
mc->eof_sent = true;
|
||||
}
|
||||
} else if (code == SS_BRK) {
|
||||
sshfwd_send_serial_break(
|
||||
mc->sc, false, 0 /* default break length */);
|
||||
} else if ((signame = ssh_signal_lookup(code)) != NULL) {
|
||||
/* It's a signal. */
|
||||
sshfwd_send_signal(mc->sc, false, signame);
|
||||
ppl_logevent("Sent signal SIG%s", signame);
|
||||
}
|
||||
}
|
||||
|
||||
void mainchan_terminal_size(mainchan *mc, int width, int height)
|
||||
{
|
||||
mc->term_width = width;
|
||||
mc->term_height = height;
|
||||
|
||||
if (mc->req_pty || mc->got_pty)
|
||||
sshfwd_send_terminal_size_change(mc->sc, width, height);
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
static ptrlen BinarySource_get_chars_internal(
|
||||
BinarySource *src, const char *set, bool include)
|
||||
{
|
||||
const char *start = here;
|
||||
while (avail(1)) {
|
||||
bool present = NULL != strchr(set, *(const char *)consume(0));
|
||||
if (present != include)
|
||||
break;
|
||||
(void) consume(1);
|
||||
}
|
||||
const char *end = here;
|
||||
return make_ptrlen(start, end - start);
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_chars(BinarySource *src, const char *include_set)
|
||||
{
|
||||
return BinarySource_get_chars_internal(src, include_set, true);
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_nonchars(BinarySource *src, const char *exclude_set)
|
||||
{
|
||||
return BinarySource_get_chars_internal(src, exclude_set, false);
|
||||
}
|
||||
|
||||
ptrlen BinarySource_get_chomped_line(BinarySource *src)
|
||||
{
|
||||
const char *start, *end;
|
||||
|
||||
if (src->err)
|
||||
return make_ptrlen(here, 0);
|
||||
|
||||
start = here;
|
||||
end = memchr(start, '\n', src->len - src->pos);
|
||||
if (end)
|
||||
advance(end + 1 - start);
|
||||
else
|
||||
advance(src->len - src->pos);
|
||||
end = here;
|
||||
|
||||
if (end > start && end[-1] == '\n')
|
||||
end--;
|
||||
if (end > start && end[-1] == '\r')
|
||||
end--;
|
||||
|
||||
return make_ptrlen(start, end - 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);
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
#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_chars(src, include) \
|
||||
BinarySource_get_chars(BinarySource_UPCAST(src), include)
|
||||
#define get_nonchars(src, exclude) \
|
||||
BinarySource_get_nonchars(BinarySource_UPCAST(src), exclude)
|
||||
#define get_chomped_line(src) \
|
||||
BinarySource_get_chomped_line(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_chars(BinarySource *, const char *include_set);
|
||||
ptrlen BinarySource_get_nonchars(BinarySource *, const char *exclude_set);
|
||||
ptrlen BinarySource_get_chomped_line(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 */
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* PuTTY's memory allocation wrappers.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "puttymem.h"
|
||||
#include "misc.h"
|
||||
|
||||
void *safemalloc(size_t factor1, size_t factor2, size_t addend)
|
||||
{
|
||||
if (factor1 > SIZE_MAX / factor2)
|
||||
goto fail;
|
||||
size_t product = factor1 * factor2;
|
||||
|
||||
if (addend > SIZE_MAX)
|
||||
goto fail;
|
||||
if (product > SIZE_MAX - addend)
|
||||
goto fail;
|
||||
size_t size = product + addend;
|
||||
|
||||
if (size == 0)
|
||||
size = 1;
|
||||
|
||||
void *p;
|
||||
#ifdef MINEFIELD
|
||||
p = minefield_c_malloc(size);
|
||||
#else
|
||||
p = malloc(size);
|
||||
#endif
|
||||
|
||||
if (!p)
|
||||
goto fail;
|
||||
|
||||
return p;
|
||||
|
||||
fail:
|
||||
out_of_memory();
|
||||
}
|
||||
|
||||
void *saferealloc(void *ptr, size_t n, size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if (n > INT_MAX / size) {
|
||||
p = NULL;
|
||||
} else {
|
||||
size *= n;
|
||||
if (!ptr) {
|
||||
#ifdef MINEFIELD
|
||||
p = minefield_c_malloc(size);
|
||||
#else
|
||||
p = malloc(size);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef MINEFIELD
|
||||
p = minefield_c_realloc(ptr, size);
|
||||
#else
|
||||
p = realloc(ptr, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (!p)
|
||||
out_of_memory();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void safefree(void *ptr)
|
||||
{
|
||||
if (ptr) {
|
||||
#ifdef MINEFIELD
|
||||
minefield_c_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void *safegrowarray(void *ptr, size_t *allocated, size_t eltsize,
|
||||
size_t oldlen, size_t extralen, bool secret)
|
||||
{
|
||||
/* The largest value we can safely multiply by eltsize */
|
||||
assert(eltsize > 0);
|
||||
size_t maxsize = (~(size_t)0) / eltsize;
|
||||
|
||||
size_t oldsize = *allocated;
|
||||
|
||||
/* Range-check the input values */
|
||||
assert(oldsize <= maxsize);
|
||||
assert(oldlen <= maxsize);
|
||||
assert(extralen <= maxsize - oldlen);
|
||||
|
||||
/* If the size is already enough, don't bother doing anything! */
|
||||
if (oldsize > oldlen + extralen)
|
||||
return ptr;
|
||||
|
||||
/* Find out how much we need to grow the array by. */
|
||||
size_t increment = (oldlen + extralen) - oldsize;
|
||||
|
||||
/* Invent a new size. We want to grow the array by at least
|
||||
* 'increment' elements; by at least a fixed number of bytes (to
|
||||
* get things started when sizes are small); and by some constant
|
||||
* factor of its old size (to avoid repeated calls to this
|
||||
* function taking quadratic time overall). */
|
||||
if (increment < 256 / eltsize)
|
||||
increment = 256 / eltsize;
|
||||
if (increment < oldsize / 16)
|
||||
increment = oldsize / 16;
|
||||
|
||||
/* But we also can't grow beyond maxsize. */
|
||||
size_t maxincr = maxsize - oldsize;
|
||||
if (increment > maxincr)
|
||||
increment = maxincr;
|
||||
|
||||
size_t newsize = oldsize + increment;
|
||||
void *toret;
|
||||
if (secret) {
|
||||
toret = safemalloc(newsize, eltsize, 0);
|
||||
if (oldsize) {
|
||||
memcpy(toret, ptr, oldsize * eltsize);
|
||||
smemclr(ptr, oldsize * eltsize);
|
||||
sfree(ptr);
|
||||
}
|
||||
} else {
|
||||
toret = saferealloc(ptr, newsize, eltsize);
|
||||
}
|
||||
*allocated = newsize;
|
||||
return toret;
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* millerrabin.c: Miller-Rabin probabilistic primality testing, as
|
||||
* declared in sshkeygen.h.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "ssh.h"
|
||||
#include "sshkeygen.h"
|
||||
#include "mpint.h"
|
||||
#include "mpunsafe.h"
|
||||
|
||||
/*
|
||||
* The Miller-Rabin primality test is an extension to the Fermat
|
||||
* test. The Fermat test just checks that a^(p-1) == 1 mod p; this
|
||||
* is vulnerable to Carmichael numbers. Miller-Rabin considers how
|
||||
* that 1 is derived as well.
|
||||
*
|
||||
* Lemma: if a^2 == 1 (mod p), and p is prime, then either a == 1
|
||||
* or a == -1 (mod p).
|
||||
*
|
||||
* Proof: p divides a^2-1, i.e. p divides (a+1)(a-1). Hence,
|
||||
* since p is prime, either p divides (a+1) or p divides (a-1).
|
||||
* But this is the same as saying that either a is congruent to
|
||||
* -1 mod p or a is congruent to +1 mod p. []
|
||||
*
|
||||
* Comment: This fails when p is not prime. Consider p=mn, so
|
||||
* that mn divides (a+1)(a-1). Now we could have m dividing (a+1)
|
||||
* and n dividing (a-1), without the whole of mn dividing either.
|
||||
* For example, consider a=10 and p=99. 99 = 9 * 11; 9 divides
|
||||
* 10-1 and 11 divides 10+1, so a^2 is congruent to 1 mod p
|
||||
* without a having to be congruent to either 1 or -1.
|
||||
*
|
||||
* So the Miller-Rabin test, as well as considering a^(p-1),
|
||||
* considers a^((p-1)/2), a^((p-1)/4), and so on as far as it can
|
||||
* go. In other words. we write p-1 as q * 2^k, with k as large as
|
||||
* possible (i.e. q must be odd), and we consider the powers
|
||||
*
|
||||
* a^(q*2^0) a^(q*2^1) ... a^(q*2^(k-1)) a^(q*2^k)
|
||||
* i.e. a^((n-1)/2^k) a^((n-1)/2^(k-1)) ... a^((n-1)/2) a^(n-1)
|
||||
*
|
||||
* If p is to be prime, the last of these must be 1. Therefore, by
|
||||
* the above lemma, the one before it must be either 1 or -1. And
|
||||
* _if_ it's 1, then the one before that must be either 1 or -1,
|
||||
* and so on ... In other words, we expect to see a trailing chain
|
||||
* of 1s preceded by a -1. (If we're unlucky, our trailing chain of
|
||||
* 1s will be as long as the list so we'll never get to see what
|
||||
* lies before it. This doesn't count as a test failure because it
|
||||
* hasn't _proved_ that p is not prime.)
|
||||
*
|
||||
* For example, consider a=2 and p=1729. 1729 is a Carmichael
|
||||
* number: although it's not prime, it satisfies a^(p-1) == 1 mod p
|
||||
* for any a coprime to it. So the Fermat test wouldn't have a
|
||||
* problem with it at all, unless we happened to stumble on an a
|
||||
* which had a common factor.
|
||||
*
|
||||
* So. 1729 - 1 equals 27 * 2^6. So we look at
|
||||
*
|
||||
* 2^27 mod 1729 == 645
|
||||
* 2^108 mod 1729 == 1065
|
||||
* 2^216 mod 1729 == 1
|
||||
* 2^432 mod 1729 == 1
|
||||
* 2^864 mod 1729 == 1
|
||||
* 2^1728 mod 1729 == 1
|
||||
*
|
||||
* We do have a trailing string of 1s, so the Fermat test would
|
||||
* have been happy. But this trailing string of 1s is preceded by
|
||||
* 1065; whereas if 1729 were prime, we'd expect to see it preceded
|
||||
* by -1 (i.e. 1728.). Guards! Seize this impostor.
|
||||
*
|
||||
* (If we were unlucky, we might have tried a=16 instead of a=2;
|
||||
* now 16^27 mod 1729 == 1, so we would have seen a long string of
|
||||
* 1s and wouldn't have seen the thing _before_ the 1s. So, just
|
||||
* like the Fermat test, for a given p there may well exist values
|
||||
* of a which fail to show up its compositeness. So we try several,
|
||||
* just like the Fermat test. The difference is that Miller-Rabin
|
||||
* is not _in general_ fooled by Carmichael numbers.)
|
||||
*
|
||||
* Put simply, then, the Miller-Rabin test requires us to:
|
||||
*
|
||||
* 1. write p-1 as q * 2^k, with q odd
|
||||
* 2. compute z = (a^q) mod p.
|
||||
* 3. report success if z == 1 or z == -1.
|
||||
* 4. square z at most k-1 times, and report success if it becomes
|
||||
* -1 at any point.
|
||||
* 5. report failure otherwise.
|
||||
*
|
||||
* (We expect z to become -1 after at most k-1 squarings, because
|
||||
* if it became -1 after k squarings then a^(p-1) would fail to be
|
||||
* 1. And we don't need to investigate what happens after we see a
|
||||
* -1, because we _know_ that -1 squared is 1 modulo anything at
|
||||
* all, so after we've seen a -1 we can be sure of seeing nothing
|
||||
* but 1s.)
|
||||
*/
|
||||
|
||||
struct MillerRabin {
|
||||
MontyContext *mc;
|
||||
|
||||
size_t k;
|
||||
mp_int *q;
|
||||
|
||||
mp_int *two, *pm1, *m_pm1;
|
||||
};
|
||||
|
||||
MillerRabin *miller_rabin_new(mp_int *p)
|
||||
{
|
||||
MillerRabin *mr = snew(MillerRabin);
|
||||
|
||||
assert(mp_hs_integer(p, 2));
|
||||
assert(mp_get_bit(p, 0) == 1);
|
||||
|
||||
mr->k = 1;
|
||||
while (!mp_get_bit(p, mr->k))
|
||||
mr->k++;
|
||||
mr->q = mp_rshift_safe(p, mr->k);
|
||||
|
||||
mr->two = mp_from_integer(2);
|
||||
|
||||
mr->pm1 = mp_unsafe_copy(p);
|
||||
mp_sub_integer_into(mr->pm1, mr->pm1, 1);
|
||||
|
||||
mr->mc = monty_new(p);
|
||||
mr->m_pm1 = monty_import(mr->mc, mr->pm1);
|
||||
|
||||
return mr;
|
||||
}
|
||||
|
||||
void miller_rabin_free(MillerRabin *mr)
|
||||
{
|
||||
mp_free(mr->q);
|
||||
mp_free(mr->two);
|
||||
mp_free(mr->pm1);
|
||||
mp_free(mr->m_pm1);
|
||||
monty_free(mr->mc);
|
||||
smemclr(mr, sizeof(*mr));
|
||||
sfree(mr);
|
||||
}
|
||||
|
||||
struct mr_result {
|
||||
bool passed;
|
||||
bool potential_primitive_root;
|
||||
};
|
||||
|
||||
static struct mr_result miller_rabin_test_inner(MillerRabin *mr, mp_int *w)
|
||||
{
|
||||
/*
|
||||
* Compute w^q mod p.
|
||||
*/
|
||||
mp_int *wqp = monty_pow(mr->mc, w, mr->q);
|
||||
|
||||
/*
|
||||
* See if this is 1, or if it is -1, or if it becomes -1
|
||||
* when squared at most k-1 times.
|
||||
*/
|
||||
struct mr_result result;
|
||||
result.passed = false;
|
||||
result.potential_primitive_root = false;
|
||||
|
||||
if (mp_cmp_eq(wqp, monty_identity(mr->mc))) {
|
||||
result.passed = true;
|
||||
} else {
|
||||
for (size_t i = 0; i < mr->k; i++) {
|
||||
if (mp_cmp_eq(wqp, mr->m_pm1)) {
|
||||
result.passed = true;
|
||||
result.potential_primitive_root = (i == mr->k - 1);
|
||||
break;
|
||||
}
|
||||
if (i == mr->k - 1)
|
||||
break;
|
||||
monty_mul_into(mr->mc, wqp, wqp, wqp);
|
||||
}
|
||||
}
|
||||
|
||||
mp_free(wqp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool miller_rabin_test_random(MillerRabin *mr)
|
||||
{
|
||||
mp_int *mw = mp_random_in_range(mr->two, mr->pm1);
|
||||
struct mr_result result = miller_rabin_test_inner(mr, mw);
|
||||
mp_free(mw);
|
||||
return result.passed;
|
||||
}
|
||||
|
||||
mp_int *miller_rabin_find_potential_primitive_root(MillerRabin *mr)
|
||||
{
|
||||
while (true) {
|
||||
mp_int *mw = mp_unsafe_shrink(mp_random_in_range(mr->two, mr->pm1));
|
||||
struct mr_result result = miller_rabin_test_inner(mr, mw);
|
||||
|
||||
if (result.passed && result.potential_primitive_root) {
|
||||
mp_int *pr = monty_export(mr->mc, mw);
|
||||
mp_free(mw);
|
||||
return pr;
|
||||
}
|
||||
|
||||
mp_free(mw);
|
||||
|
||||
if (!result.passed) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned miller_rabin_checks_needed(unsigned bits)
|
||||
{
|
||||
/* Table 4.4 from Handbook of Applied Cryptography */
|
||||
return (bits >= 1300 ? 2 : bits >= 850 ? 3 : bits >= 650 ? 4 :
|
||||
bits >= 550 ? 5 : bits >= 450 ? 6 : bits >= 400 ? 7 :
|
||||
bits >= 350 ? 8 : bits >= 300 ? 9 : bits >= 250 ? 12 :
|
||||
bits >= 200 ? 15 : bits >= 150 ? 18 : 27);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#define BASE64_CHARS_NOEQ \
|
||||
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
|
||||
#define BASE64_CHARS_ALL BASE64_CHARS_NOEQ "="
|
||||
|
||||
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 (strstartswith(q, "SHA256:")) {
|
||||
/* Test for a valid SHA256 key fingerprint. */
|
||||
r = q + 7;
|
||||
if (strlen(r) == 43 && r[strspn(r, BASE64_CHARS_NOEQ)] == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
r = q;
|
||||
if (strstartswith(r, "MD5:"))
|
||||
r += 4;
|
||||
if (strlen(r) == 16*3 - 1 &&
|
||||
r[strspn(r, "0123456789abcdefABCDEF:")] == 0) {
|
||||
/*
|
||||
* Test for a valid MD5 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 (r[3*i] == ':' || r[3*i+1] == ':')
|
||||
goto not_fingerprint; /* sorry */
|
||||
for (i = 0; i < 15; i++)
|
||||
if (r[3*i+2] != ':')
|
||||
goto not_fingerprint; /* sorry */
|
||||
for (i = 0; i < 16*3 - 1; i++)
|
||||
key[i] = tolower(r[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, BASE64_CHARS_ALL)] == 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 == 1928 && _MSC_FULL_VER >= 192829500
|
||||
/*
|
||||
* 16.9 and 16.8 have the same _MSC_VER value, and have to be
|
||||
* distinguished by _MSC_FULL_VER. As of 2021-03-04 that is not
|
||||
* mentioned on the above page, but see e.g.
|
||||
* https://developercommunity.visualstudio.com/t/the-169-cc-compiler-still-uses-the-same-version-nu/1335194#T-N1337120
|
||||
* which says that 16.9 builds will have versions starting at
|
||||
* 19.28.29500.* and going up. Hence, 19 28 29500 is what we
|
||||
* compare _MSC_FULL_VER against above.
|
||||
*/
|
||||
strbuf_catf(buf, " 2019 (16.9)");
|
||||
#elif _MSC_VER == 1928
|
||||
strbuf_catf(buf, " 2019 (16.8)");
|
||||
#elif _MSC_VER == 1927
|
||||
strbuf_catf(buf, " 2019 (16.7)");
|
||||
#elif _MSC_VER == 1926
|
||||
strbuf_catf(buf, " 2019 (16.6)");
|
||||
#elif _MSC_VER == 1925
|
||||
strbuf_catf(buf, " 2019 (16.5)");
|
||||
#elif _MSC_VER == 1924
|
||||
strbuf_catf(buf, " 2019 (16.4)");
|
||||
#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, const char *keydisp, char **key_fingerprints,
|
||||
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; }
|
||||
bool nullseat_verbose_no(Seat *seat) { return false; }
|
||||
bool nullseat_verbose_yes(Seat *seat) { return true; }
|
||||
bool nullseat_interactive_no(Seat *seat) { return false; }
|
||||
bool nullseat_interactive_yes(Seat *seat) { return true; }
|
||||
bool nullseat_get_cursor_position(Seat *seat, int *x, int *y) { return false; }
|
||||
|
||||
bool null_lp_verbose_no(LogPolicy *lp) { return false; }
|
||||
bool null_lp_verbose_yes(LogPolicy *lp) { 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");
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* 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 compile-time string literal in a way that
|
||||
* allows you to declare the ptrlen itself as a compile-time initialiser. */
|
||||
#define PTRLEN_DECL_LITERAL(stringlit) \
|
||||
{ TYPECHECK("" stringlit "", 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);
|
||||
|
||||
/* Write a string out in C string-literal format. */
|
||||
void write_c_string_literal(FILE *fp, ptrlen str);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/*
|
||||
* A mechanism for loading a file from disk into a memory buffer where
|
||||
* it can be picked apart as a BinarySource.
|
||||
*/
|
||||
struct LoadedFile {
|
||||
char *data;
|
||||
size_t len, max_size;
|
||||
BinarySource_IMPLEMENTATION;
|
||||
};
|
||||
typedef enum {
|
||||
LF_OK, /* file loaded successfully */
|
||||
LF_TOO_BIG, /* file didn't fit in buffer */
|
||||
LF_ERROR, /* error from stdio layer */
|
||||
} LoadFileStatus;
|
||||
LoadedFile *lf_new(size_t max_size);
|
||||
void lf_free(LoadedFile *lf);
|
||||
LoadFileStatus lf_load_fp(LoadedFile *lf, FILE *fp);
|
||||
LoadFileStatus lf_load(LoadedFile *lf, const Filename *filename);
|
||||
static inline ptrlen ptrlen_from_lf(LoadedFile *lf)
|
||||
{ return make_ptrlen(lf->data, lf->len); }
|
||||
|
||||
/* Set the memory block of 'size' bytes at 'out' to the bitwise XOR of
|
||||
* the two blocks of the same size at 'in1' and 'in2'.
|
||||
*
|
||||
* 'out' may point to exactly the same address as one of the inputs,
|
||||
* but if the input and output blocks overlap in any other way, the
|
||||
* result of this function is not guaranteed. No memmove-style effort
|
||||
* is made to handle difficult overlap cases. */
|
||||
void memxor(uint8_t *out, const uint8_t *in1, const uint8_t *in2, size_t size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Centralised Unicode-related helper functions, separate from misc.c
|
||||
* so that they can be omitted from tools that aren't including
|
||||
* Unicode handling.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
#include "misc.h"
|
||||
|
||||
wchar_t *dup_mb_to_wc_c(int codepage, int flags, const char *string, int len)
|
||||
{
|
||||
int mult;
|
||||
for (mult = 1 ;; mult++) {
|
||||
wchar_t *ret = snewn(mult*len + 2, wchar_t);
|
||||
int outlen;
|
||||
outlen = mb_to_wc(codepage, flags, string, len, ret, mult*len + 1);
|
||||
if (outlen < mult*len+1) {
|
||||
ret[outlen] = L'\0';
|
||||
return ret;
|
||||
}
|
||||
sfree(ret);
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string)
|
||||
{
|
||||
return dup_mb_to_wc_c(codepage, flags, string, strlen(string));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,445 @@
|
||||
#ifndef PUTTY_MPINT_H
|
||||
#define PUTTY_MPINT_H
|
||||
|
||||
/*
|
||||
* PuTTY's multiprecision integer library.
|
||||
*
|
||||
* This library is written with the aim of avoiding leaking the input
|
||||
* numbers via timing and cache side channels. This means avoiding
|
||||
* making any control flow change, or deciding the address of any
|
||||
* memory access, based on the value of potentially secret input data.
|
||||
*
|
||||
* But in a library that has to handle numbers of arbitrary size, you
|
||||
* can't avoid your control flow depending on the _size_ of the input!
|
||||
* So the rule is that an mp_int has a nominal size that need not be
|
||||
* its mathematical size: i.e. if you call (say) mp_from_bytes_be to
|
||||
* turn an array of 256 bytes into an integer, and all but the last of
|
||||
* those bytes is zero, then you get an mp_int which has space for 256
|
||||
* bytes of data but just happens to store the value 1. So the
|
||||
* _nominal_ sizes of input data - e.g. the size in bits of some
|
||||
* public-key modulus - are not considered secret, and control flow is
|
||||
* allowed to do what it likes based on those sizes. But the same
|
||||
* function, called with the same _nominally sized_ arguments
|
||||
* containing different values, should run in the same length of time.
|
||||
*
|
||||
* When a function returns an 'mp_int *', it is newly allocated to an
|
||||
* appropriate nominal size (which, again, depends only on the nominal
|
||||
* sizes of the inputs). Other functions have 'into' in their name,
|
||||
* and they instead overwrite the contents of an existing mp_int.
|
||||
*
|
||||
* Functions in this API which return values that are logically
|
||||
* boolean return them as 'unsigned' rather than the C99 bool type.
|
||||
* That's because C99 bool does an implicit test for non-zero-ness
|
||||
* when converting any other integer type to it, which compilers might
|
||||
* well implement using data-dependent control flow.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Create and destroy mp_ints. A newly created one is initialised to
|
||||
* zero. mp_clear also resets an existing number to zero.
|
||||
*/
|
||||
mp_int *mp_new(size_t maxbits);
|
||||
void mp_free(mp_int *);
|
||||
void mp_clear(mp_int *x);
|
||||
|
||||
/*
|
||||
* Create mp_ints from various sources: little- and big-endian binary
|
||||
* data, an ordinary C unsigned integer type, a decimal or hex string
|
||||
* (given either as a ptrlen or a C NUL-terminated string), and
|
||||
* another mp_int.
|
||||
*
|
||||
* The decimal and hex conversion functions have running time
|
||||
* dependent on the length of the input data, of course.
|
||||
*/
|
||||
mp_int *mp_from_bytes_le(ptrlen bytes);
|
||||
mp_int *mp_from_bytes_be(ptrlen bytes);
|
||||
mp_int *mp_from_integer(uintmax_t n);
|
||||
mp_int *mp_from_decimal_pl(ptrlen decimal);
|
||||
mp_int *mp_from_decimal(const char *decimal);
|
||||
mp_int *mp_from_hex_pl(ptrlen hex);
|
||||
mp_int *mp_from_hex(const char *hex);
|
||||
mp_int *mp_copy(mp_int *x);
|
||||
|
||||
/*
|
||||
* A macro for declaring large fixed numbers in source code (such as
|
||||
* elliptic curve parameters, or standard Diffie-Hellman moduli). The
|
||||
* idea is that you just write something like
|
||||
*
|
||||
* mp_int *value = MP_LITERAL(0x19284376283754638745693467245);
|
||||
*
|
||||
* and it newly allocates you an mp_int containing that number.
|
||||
*
|
||||
* Internally, the macro argument is stringified and passed to
|
||||
* mp_from_hex. That's not as fast as it could be if I had instead set
|
||||
* up some kind of mp_from_array_of_uint64_t() function, but I think
|
||||
* this system is valuable for the fact that the literal integers
|
||||
* appear in a very natural syntax that can be pasted directly out
|
||||
* into, say, Python if you want to cross-check a calculation.
|
||||
*/
|
||||
static inline mp_int *mp__from_string_literal(const char *lit)
|
||||
{
|
||||
/* Don't call this directly; it's not equipped to deal with
|
||||
* hostile data. Use only via the MP_LITERAL macro. */
|
||||
if (lit[0] && (lit[1] == 'x' || lit[1] == 'X'))
|
||||
return mp_from_hex(lit+2);
|
||||
else
|
||||
return mp_from_decimal(lit);
|
||||
}
|
||||
#define MP_LITERAL(number) mp__from_string_literal(#number)
|
||||
|
||||
/*
|
||||
* Create an mp_int with the value 2^power.
|
||||
*/
|
||||
mp_int *mp_power_2(size_t power);
|
||||
|
||||
/*
|
||||
* Retrieve the value of a particular bit or byte of an mp_int. The
|
||||
* byte / bit index is not considered to be secret data. Out-of-range
|
||||
* byte/bit indices are handled cleanly and return zero.
|
||||
*/
|
||||
uint8_t mp_get_byte(mp_int *x, size_t byte);
|
||||
unsigned mp_get_bit(mp_int *x, size_t bit);
|
||||
|
||||
/*
|
||||
* Retrieve the value of an mp_int as a uintmax_t, assuming it's small
|
||||
* enough to fit.
|
||||
*/
|
||||
uintmax_t mp_get_integer(mp_int *x);
|
||||
|
||||
/*
|
||||
* Set an mp_int bit. Again, the bit index is not considered secret.
|
||||
* Do not pass an out-of-range index, on pain of assertion failure.
|
||||
*/
|
||||
void mp_set_bit(mp_int *x, size_t bit, unsigned val);
|
||||
|
||||
/*
|
||||
* Return the nominal size of an mp_int, in terms of the maximum
|
||||
* number of bytes or bits that can fit in it.
|
||||
*/
|
||||
size_t mp_max_bytes(mp_int *x);
|
||||
size_t mp_max_bits(mp_int *x);
|
||||
|
||||
/*
|
||||
* Return the _mathematical_ bit count of an mp_int (not its nominal
|
||||
* size), i.e. a value n such that 2^{n-1} <= x < 2^n.
|
||||
*
|
||||
* This function is supposed to run in constant time for a given
|
||||
* nominal input size. Of course it's likely that clients of this
|
||||
* function will promptly need to use the result as the limit of some
|
||||
* loop (e.g. marshalling an mp_int into an SSH packet, which doesn't
|
||||
* permit extra prefix zero bytes). But that's up to the caller to
|
||||
* decide the safety of.
|
||||
*/
|
||||
size_t mp_get_nbits(mp_int *x);
|
||||
|
||||
/*
|
||||
* Return the value of an mp_int as a decimal or hex string. The
|
||||
* result is dynamically allocated, and the caller is responsible for
|
||||
* freeing it.
|
||||
*
|
||||
* These functions should run in constant time for a given nominal
|
||||
* input size, even though the exact number of digits returned is
|
||||
* variable. They always allocate enough space for the largest output
|
||||
* that might be needed, but they don't always fill it.
|
||||
*/
|
||||
char *mp_get_decimal(mp_int *x);
|
||||
char *mp_get_hex(mp_int *x);
|
||||
char *mp_get_hex_uppercase(mp_int *x);
|
||||
|
||||
/*
|
||||
* Compare two mp_ints, or compare one mp_int against a C integer. The
|
||||
* 'eq' functions return 1 if the two inputs are equal, or 0
|
||||
* otherwise; the 'hs' functions return 1 if the first input is >= the
|
||||
* second, and 0 otherwise.
|
||||
*/
|
||||
unsigned mp_cmp_hs(mp_int *a, mp_int *b);
|
||||
unsigned mp_cmp_eq(mp_int *a, mp_int *b);
|
||||
unsigned mp_hs_integer(mp_int *x, uintmax_t n);
|
||||
unsigned mp_eq_integer(mp_int *x, uintmax_t n);
|
||||
|
||||
/*
|
||||
* Take the minimum or maximum of two mp_ints, without using a
|
||||
* conditional branch.
|
||||
*/
|
||||
void mp_min_into(mp_int *r, mp_int *x, mp_int *y);
|
||||
void mp_max_into(mp_int *r, mp_int *x, mp_int *y);
|
||||
mp_int *mp_min(mp_int *x, mp_int *y);
|
||||
mp_int *mp_max(mp_int *x, mp_int *y);
|
||||
|
||||
/*
|
||||
* Diagnostic function. Writes out x in hex to the supplied stdio
|
||||
* stream, preceded by the string 'prefix' and followed by 'suffix'.
|
||||
*
|
||||
* This is useful to put temporarily into code, but it's also
|
||||
* potentially useful to call from a debugger.
|
||||
*/
|
||||
void mp_dump(FILE *fp, const char *prefix, mp_int *x, const char *suffix);
|
||||
|
||||
/*
|
||||
* Overwrite one mp_int with another, or with a plain integer.
|
||||
*/
|
||||
void mp_copy_into(mp_int *dest, mp_int *src);
|
||||
void mp_copy_integer_into(mp_int *dest, uintmax_t n);
|
||||
|
||||
/*
|
||||
* Conditional selection. Overwrites dest with either src0 or src1,
|
||||
* according to the value of 'choose_src1'. choose_src1 should be 0 or
|
||||
* 1; if it's 1, then dest is set to src1, otherwise src0.
|
||||
*
|
||||
* The value of choose_src1 is considered to be secret data, so
|
||||
* control flow and memory access should not depend on it.
|
||||
*/
|
||||
void mp_select_into(mp_int *dest, mp_int *src0, mp_int *src1,
|
||||
unsigned choose_src1);
|
||||
|
||||
/*
|
||||
* Addition, subtraction and multiplication, either targeting an
|
||||
* existing mp_int or making a new one large enough to hold whatever
|
||||
* the output might be..
|
||||
*/
|
||||
void mp_add_into(mp_int *r, mp_int *a, mp_int *b);
|
||||
void mp_sub_into(mp_int *r, mp_int *a, mp_int *b);
|
||||
void mp_mul_into(mp_int *r, mp_int *a, mp_int *b);
|
||||
mp_int *mp_add(mp_int *x, mp_int *y);
|
||||
mp_int *mp_sub(mp_int *x, mp_int *y);
|
||||
mp_int *mp_mul(mp_int *x, mp_int *y);
|
||||
|
||||
/*
|
||||
* Bitwise operations.
|
||||
*/
|
||||
void mp_and_into(mp_int *r, mp_int *a, mp_int *b);
|
||||
void mp_or_into(mp_int *r, mp_int *a, mp_int *b);
|
||||
void mp_xor_into(mp_int *r, mp_int *a, mp_int *b);
|
||||
void mp_bic_into(mp_int *r, mp_int *a, mp_int *b);
|
||||
|
||||
/*
|
||||
* Addition, subtraction and multiplication with one argument small
|
||||
* enough to fit in a C integer. For mp_mul_integer_into, it has to be
|
||||
* even smaller than that.
|
||||
*/
|
||||
void mp_add_integer_into(mp_int *r, mp_int *a, uintmax_t n);
|
||||
void mp_sub_integer_into(mp_int *r, mp_int *a, uintmax_t n);
|
||||
void mp_mul_integer_into(mp_int *r, mp_int *a, uint16_t n);
|
||||
|
||||
/*
|
||||
* Conditional addition/subtraction. If yes == 1, sets r to a+b or a-b
|
||||
* (respectively). If yes == 0, sets r to just a. 'yes' is considered
|
||||
* secret data.
|
||||
*/
|
||||
void mp_cond_add_into(mp_int *r, mp_int *a, mp_int *b, unsigned yes);
|
||||
void mp_cond_sub_into(mp_int *r, mp_int *a, mp_int *b, unsigned yes);
|
||||
|
||||
/*
|
||||
* Swap x0 and x1 if swap == 1, and not if swap == 0. 'swap' is
|
||||
* considered secret.
|
||||
*/
|
||||
void mp_cond_swap(mp_int *x0, mp_int *x1, unsigned swap);
|
||||
|
||||
/*
|
||||
* Set x to 0 if clear == 1, and otherwise leave it unchanged. 'clear'
|
||||
* is considered secret.
|
||||
*/
|
||||
void mp_cond_clear(mp_int *x, unsigned clear);
|
||||
|
||||
/*
|
||||
* Division. mp_divmod_into divides n by d, and writes the quotient
|
||||
* into q and the remainder into r. You can pass either of q and r as
|
||||
* NULL if you don't need one of the outputs.
|
||||
*
|
||||
* mp_div and mp_mod are wrappers that return one or other of those
|
||||
* outputs as a freshly allocated mp_int of the appropriate size.
|
||||
*
|
||||
* Division by zero gives no error, and returns a quotient of 0 and a
|
||||
* remainder of n (so as to still satisfy the division identity that
|
||||
* n=qd+r).
|
||||
*/
|
||||
void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q, mp_int *r);
|
||||
mp_int *mp_div(mp_int *n, mp_int *d);
|
||||
mp_int *mp_mod(mp_int *x, mp_int *modulus);
|
||||
|
||||
/*
|
||||
* Integer nth root. mp_nthroot returns the largest integer x such
|
||||
* that x^n <= y, and if 'remainder' is non-NULL then it fills it with
|
||||
* the residue (y - x^n).
|
||||
*
|
||||
* Currently, n has to be small enough that the largest binomial
|
||||
* coefficient (n choose k) fits in 16 bits, which works out to at
|
||||
* most 18.
|
||||
*/
|
||||
mp_int *mp_nthroot(mp_int *y, unsigned n, mp_int *remainder);
|
||||
|
||||
/*
|
||||
* Trivially easy special case of mp_mod: reduce a number mod a power
|
||||
* of two.
|
||||
*/
|
||||
void mp_reduce_mod_2to(mp_int *x, size_t p);
|
||||
|
||||
/*
|
||||
* Modular inverses. mp_invert computes the inverse of x mod modulus
|
||||
* (and will expect the two to be coprime). mp_invert_mod_2to computes
|
||||
* the inverse of x mod 2^p, and is a great deal faster.
|
||||
*/
|
||||
mp_int *mp_invert_mod_2to(mp_int *x, size_t p);
|
||||
mp_int *mp_invert(mp_int *x, mp_int *modulus);
|
||||
|
||||
/*
|
||||
* Greatest common divisor.
|
||||
*
|
||||
* mp_gcd_into also returns a pair of Bezout coefficients, namely A,B
|
||||
* such that a*A - b*B = gcd. (The minus sign is so that both returned
|
||||
* coefficients can be positive.)
|
||||
*
|
||||
* You can pass any of mp_gcd_into's output pointers as NULL if you
|
||||
* don't need that output value.
|
||||
*
|
||||
* mp_gcd is a wrapper with a less cumbersome API, for the case where
|
||||
* the only output value you need is the gcd itself. mp_coprime is
|
||||
* even easier, if all you care about is whether or not that gcd is 1.
|
||||
*/
|
||||
mp_int *mp_gcd(mp_int *a, mp_int *b);
|
||||
void mp_gcd_into(mp_int *a, mp_int *b,
|
||||
mp_int *gcd_out, mp_int *A_out, mp_int *B_out);
|
||||
unsigned mp_coprime(mp_int *a, mp_int *b);
|
||||
|
||||
/*
|
||||
* System for taking square roots modulo an odd prime.
|
||||
*
|
||||
* In order to do this efficiently, you need to provide an extra piece
|
||||
* of information at setup time, namely a number which is not
|
||||
* congruent mod p to any square. Given p and that non-square, you can
|
||||
* use modsqrt_new to make a context containing all the necessary
|
||||
* equipment for actually calculating the square roots, and then you
|
||||
* can call mp_modsqrt as many times as you like on that context
|
||||
* before freeing it.
|
||||
*
|
||||
* The output parameter '*success' will be filled in with 1 if the
|
||||
* operation was successful, or 0 if the input number doesn't have a
|
||||
* square root mod p at all. In the latter case, the returned mp_int
|
||||
* will be nonsense and you shouldn't depend on it.
|
||||
*
|
||||
* ==== WARNING ====
|
||||
*
|
||||
* This function DOES NOT TREAT THE PRIME MODULUS AS SECRET DATA! It
|
||||
* will protect the number you're taking the square root _of_, but not
|
||||
* the number you're taking the root of it _mod_.
|
||||
*
|
||||
* (This is because the algorithm requires a number of loop iterations
|
||||
* equal to the number of factors of 2 in p-1. And the expected use of
|
||||
* this function is for elliptic-curve point decompression, in which
|
||||
* the modulus is always a well-known one written down in standards
|
||||
* documents.)
|
||||
*/
|
||||
typedef struct ModsqrtContext ModsqrtContext;
|
||||
ModsqrtContext *modsqrt_new(mp_int *p, mp_int *any_nonsquare_mod_p);
|
||||
void modsqrt_free(ModsqrtContext *);
|
||||
mp_int *mp_modsqrt(ModsqrtContext *sc, mp_int *x, unsigned *success);
|
||||
|
||||
/*
|
||||
* Functions for Montgomery multiplication, a fast technique for doing
|
||||
* a long series of modular multiplications all with the same modulus
|
||||
* (which has to be odd).
|
||||
*
|
||||
* You start by calling monty_new to set up a context structure
|
||||
* containing all the precomputed bits and pieces needed by the
|
||||
* algorithm. Then, any numbers you want to work with must first be
|
||||
* transformed into the internal Montgomery representation using
|
||||
* monty_import; having done that, you can use monty_mul and monty_pow
|
||||
* to operate on them efficiently; and finally, monty_export will
|
||||
* convert numbers back out of Montgomery representation to give their
|
||||
* ordinary values.
|
||||
*
|
||||
* Addition and subtraction are not optimised by the Montgomery trick,
|
||||
* but monty_add and monty_sub are provided anyway for convenience.
|
||||
*
|
||||
* There are also monty_invert and monty_modsqrt, which are analogues
|
||||
* of mp_invert and mp_modsqrt which take their inputs in Montgomery
|
||||
* representation. For mp_modsqrt, the prime modulus of the
|
||||
* ModsqrtContext must be the same as the modulus of the MontyContext.
|
||||
*
|
||||
* The query functions monty_modulus and monty_identity return numbers
|
||||
* stored inside the MontyContext, without copying them. The returned
|
||||
* pointers are still owned by the MontyContext, so don't free them!
|
||||
*/
|
||||
MontyContext *monty_new(mp_int *modulus);
|
||||
void monty_free(MontyContext *mc);
|
||||
mp_int *monty_modulus(MontyContext *mc); /* doesn't transfer ownership */
|
||||
mp_int *monty_identity(MontyContext *mc); /* doesn't transfer ownership */
|
||||
void monty_import_into(MontyContext *mc, mp_int *r, mp_int *x);
|
||||
mp_int *monty_import(MontyContext *mc, mp_int *x);
|
||||
void monty_export_into(MontyContext *mc, mp_int *r, mp_int *x);
|
||||
mp_int *monty_export(MontyContext *mc, mp_int *x);
|
||||
void monty_mul_into(MontyContext *, mp_int *r, mp_int *, mp_int *);
|
||||
mp_int *monty_add(MontyContext *, mp_int *, mp_int *);
|
||||
mp_int *monty_sub(MontyContext *, mp_int *, mp_int *);
|
||||
mp_int *monty_mul(MontyContext *, mp_int *, mp_int *);
|
||||
mp_int *monty_pow(MontyContext *, mp_int *base, mp_int *exponent);
|
||||
mp_int *monty_invert(MontyContext *, mp_int *);
|
||||
mp_int *monty_modsqrt(ModsqrtContext *sc, mp_int *mx, unsigned *success);
|
||||
|
||||
/*
|
||||
* Modular arithmetic functions which don't use an explicit
|
||||
* MontyContext. mp_modpow will use one internally (on the assumption
|
||||
* that the exponent is likely to be large enough to make it
|
||||
* worthwhile); the other three will just do ordinary non-Montgomery-
|
||||
* optimised modular reduction. Use mp_modmul if you only have one
|
||||
* product to compute; if you have a lot, consider using a
|
||||
* MontyContext in the client code.
|
||||
*/
|
||||
mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus);
|
||||
mp_int *mp_modmul(mp_int *x, mp_int *y, mp_int *modulus);
|
||||
mp_int *mp_modadd(mp_int *x, mp_int *y, mp_int *modulus);
|
||||
mp_int *mp_modsub(mp_int *x, mp_int *y, mp_int *modulus);
|
||||
|
||||
/*
|
||||
* Shift an mp_int by a given number of bits. The shift count is
|
||||
* considered to be secret data, and as a result, the algorithm takes
|
||||
* O(n log n) time instead of the obvious O(n).
|
||||
*
|
||||
* There's no mp_lshift_safe, because the size of mp_int to allocate
|
||||
* would not be able to avoid depending on the shift count. So if you
|
||||
* need to behave independently of the size of a left shift, you have
|
||||
* to know a bound on the space you'll need by some other means.
|
||||
*/
|
||||
void mp_lshift_safe_into(mp_int *r, mp_int *x, size_t shift);
|
||||
void mp_rshift_safe_into(mp_int *r, mp_int *x, size_t shift);
|
||||
mp_int *mp_rshift_safe(mp_int *x, size_t shift);
|
||||
|
||||
/*
|
||||
* Shift an mp_int left or right by a fixed number of bits. The shift
|
||||
* count is NOT considered to be secret data! Use this if you're
|
||||
* always dividing by 2, for example, but don't use it to shift by a
|
||||
* variable amount derived from another secret number.
|
||||
*
|
||||
* The upside is that these functions run in sensible linear time.
|
||||
*/
|
||||
void mp_lshift_fixed_into(mp_int *r, mp_int *a, size_t shift);
|
||||
void mp_rshift_fixed_into(mp_int *r, mp_int *x, size_t shift);
|
||||
mp_int *mp_lshift_fixed(mp_int *x, size_t shift);
|
||||
mp_int *mp_rshift_fixed(mp_int *x, size_t shift);
|
||||
|
||||
/*
|
||||
* Generate a random mp_int.
|
||||
*
|
||||
* The _function_ definitions here will expect to be given a gen_data
|
||||
* function that provides random data. Normally you'd use this using
|
||||
* random_read() from random.c, and the macro wrappers automate that.
|
||||
*
|
||||
* (This is a bit of a dodge to avoid mpint.c having a link-time
|
||||
* dependency on random.c, so that programs can link against one but
|
||||
* not the other: if a client of this header uses one of these macros
|
||||
* then _they_ have link-time dependencies on both modules.)
|
||||
*
|
||||
* mp_random_bits[_fn] returns an integer 0 <= n < 2^bits.
|
||||
* mp_random_upto[_fn](limit) returns an integer 0 <= n < limit.
|
||||
* mp_random_in_range[_fn](lo,hi) returns an integer lo <= n < hi.
|
||||
*/
|
||||
typedef void (*random_read_fn_t)(void *, size_t);
|
||||
mp_int *mp_random_bits_fn(size_t bits, random_read_fn_t randfn);
|
||||
mp_int *mp_random_upto_fn(mp_int *limit, random_read_fn_t randfn);
|
||||
mp_int *mp_random_in_range_fn(
|
||||
mp_int *lo_inclusive, mp_int *hi_exclusive, random_read_fn_t randfn);
|
||||
#define mp_random_bits(bits) mp_random_bits_fn(bits, random_read)
|
||||
#define mp_random_upto(limit) mp_random_upto_fn(limit, random_read)
|
||||
#define mp_random_in_range(lo, hi) mp_random_in_range_fn(lo, hi, random_read)
|
||||
|
||||
#endif /* PUTTY_MPINT_H */
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* mpint_i.h: definitions used internally by the bignum code, and
|
||||
* also a few other vaguely-bignum-like places.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* The assorted conditional definitions of BignumInt and multiply
|
||||
* macros used throughout the bignum code to treat numbers as arrays
|
||||
* of the most conveniently sized word for the target machine.
|
||||
* Exported so that other code (e.g. poly1305) can use it too.
|
||||
*
|
||||
* This code must export, in whatever ifdef branch it ends up in:
|
||||
*
|
||||
* - two types: 'BignumInt' and 'BignumCarry'. BignumInt is an
|
||||
* unsigned integer type which will be used as the base word size
|
||||
* for all bignum operations. BignumCarry is an unsigned integer
|
||||
* type used to hold the carry flag taken as input and output by
|
||||
* the BignumADC macro (see below).
|
||||
*
|
||||
* - five constant macros:
|
||||
* + BIGNUM_INT_BITS, the number of bits in BignumInt,
|
||||
* + BIGNUM_INT_BYTES, the number of bytes that works out to
|
||||
* + BIGNUM_TOP_BIT, the BignumInt value consisting of only the top bit
|
||||
* + BIGNUM_INT_MASK, the BignumInt value with all bits set
|
||||
* + BIGNUM_INT_BITS_BITS, log to the base 2 of BIGNUM_INT_BITS.
|
||||
*
|
||||
* - four statement macros: BignumADC, BignumMUL, BignumMULADD,
|
||||
* BignumMULADD2. These do various kinds of multi-word arithmetic,
|
||||
* and all produce two output values.
|
||||
* * BignumADC(ret,retc,a,b,c) takes input BignumInt values a,b
|
||||
* and a BignumCarry c, and outputs a BignumInt ret = a+b+c and
|
||||
* a BignumCarry retc which is the carry off the top of that
|
||||
* addition.
|
||||
* * BignumMUL(rh,rl,a,b) returns the two halves of the
|
||||
* double-width product a*b.
|
||||
* * BignumMULADD(rh,rl,a,b,addend) returns the two halves of the
|
||||
* double-width value a*b + addend.
|
||||
* * BignumMULADD2(rh,rl,a,b,addend1,addend2) returns the two
|
||||
* halves of the double-width value a*b + addend1 + addend2.
|
||||
*
|
||||
* Every branch of the main ifdef below defines the type BignumInt and
|
||||
* the value BIGNUM_INT_BITS_BITS. The other constant macros are
|
||||
* filled in by common code further down.
|
||||
*
|
||||
* Most branches also define a macro DEFINE_BIGNUMDBLINT containing a
|
||||
* typedef statement which declares a type _twice_ the length of a
|
||||
* BignumInt. This causes the common code further down to produce a
|
||||
* default implementation of the four statement macros in terms of
|
||||
* that double-width type, and also to defined BignumCarry to be
|
||||
* BignumInt.
|
||||
*
|
||||
* However, if a particular compile target does not have a type twice
|
||||
* the length of the BignumInt you want to use but it does provide
|
||||
* some alternative means of doing add-with-carry and double-word
|
||||
* multiply, then the ifdef branch in question can just define
|
||||
* BignumCarry and the four statement macros itself, and that's fine
|
||||
* too.
|
||||
*/
|
||||
|
||||
/* You can lower the BignumInt size by defining BIGNUM_OVERRIDE on the
|
||||
* command line to be your chosen max value of BIGNUM_INT_BITS_BITS */
|
||||
#if defined BIGNUM_OVERRIDE
|
||||
#define BB_OK(b) ((b) <= BIGNUM_OVERRIDE)
|
||||
#else
|
||||
#define BB_OK(b) (1)
|
||||
#endif
|
||||
|
||||
#if defined __SIZEOF_INT128__ && BB_OK(6)
|
||||
|
||||
/*
|
||||
* 64-bit BignumInt using gcc/clang style 128-bit BignumDblInt.
|
||||
*
|
||||
* gcc and clang both provide a __uint128_t type on 64-bit targets
|
||||
* (and, when they do, indicate its presence by the above macro),
|
||||
* using the same 'two machine registers' kind of code generation
|
||||
* that 32-bit targets use for 64-bit ints.
|
||||
*/
|
||||
|
||||
typedef unsigned long long BignumInt;
|
||||
#define BIGNUM_INT_BITS_BITS 6
|
||||
#define DEFINE_BIGNUMDBLINT typedef __uint128_t BignumDblInt
|
||||
|
||||
#elif defined _MSC_VER && defined _M_AMD64 && BB_OK(6)
|
||||
|
||||
/*
|
||||
* 64-bit BignumInt, using Visual Studio x86-64 compiler intrinsics.
|
||||
*
|
||||
* 64-bit Visual Studio doesn't provide very much in the way of help
|
||||
* here: there's no int128 type, and also no inline assembler giving
|
||||
* us direct access to the x86-64 MUL or ADC instructions. However,
|
||||
* there are compiler intrinsics giving us that access, so we can
|
||||
* use those - though it turns out we have to be a little careful,
|
||||
* since they seem to generate wrong code if their pointer-typed
|
||||
* output parameters alias their inputs. Hence all the internal temp
|
||||
* variables inside the macros.
|
||||
*/
|
||||
|
||||
#include <intrin.h>
|
||||
typedef unsigned char BignumCarry; /* the type _addcarry_u64 likes to use */
|
||||
typedef unsigned __int64 BignumInt;
|
||||
#define BIGNUM_INT_BITS_BITS 6
|
||||
#define BignumADC(ret, retc, a, b, c) do \
|
||||
{ \
|
||||
BignumInt ADC_tmp; \
|
||||
(retc) = _addcarry_u64(c, a, b, &ADC_tmp); \
|
||||
(ret) = ADC_tmp; \
|
||||
} while (0)
|
||||
#define BignumMUL(rh, rl, a, b) do \
|
||||
{ \
|
||||
BignumInt MULADD_hi; \
|
||||
(rl) = _umul128(a, b, &MULADD_hi); \
|
||||
(rh) = MULADD_hi; \
|
||||
} while (0)
|
||||
#define BignumMULADD(rh, rl, a, b, addend) do \
|
||||
{ \
|
||||
BignumInt MULADD_lo, MULADD_hi; \
|
||||
MULADD_lo = _umul128(a, b, &MULADD_hi); \
|
||||
MULADD_hi += _addcarry_u64(0, MULADD_lo, (addend), &(rl)); \
|
||||
(rh) = MULADD_hi; \
|
||||
} while (0)
|
||||
#define BignumMULADD2(rh, rl, a, b, addend1, addend2) do \
|
||||
{ \
|
||||
BignumInt MULADD_lo1, MULADD_lo2, MULADD_hi; \
|
||||
MULADD_lo1 = _umul128(a, b, &MULADD_hi); \
|
||||
MULADD_hi += _addcarry_u64(0, MULADD_lo1, (addend1), &MULADD_lo2); \
|
||||
MULADD_hi += _addcarry_u64(0, MULADD_lo2, (addend2), &(rl)); \
|
||||
(rh) = MULADD_hi; \
|
||||
} while (0)
|
||||
|
||||
#elif (defined __GNUC__ || defined _LLP64 || __STDC__ >= 199901L) && BB_OK(5)
|
||||
|
||||
/* 32-bit BignumInt, using C99 unsigned long long as BignumDblInt */
|
||||
|
||||
typedef unsigned int BignumInt;
|
||||
#define BIGNUM_INT_BITS_BITS 5
|
||||
#define DEFINE_BIGNUMDBLINT typedef unsigned long long BignumDblInt
|
||||
|
||||
#elif defined _MSC_VER && BB_OK(5)
|
||||
|
||||
/* 32-bit BignumInt, using Visual Studio __int64 as BignumDblInt */
|
||||
|
||||
typedef unsigned int BignumInt;
|
||||
#define BIGNUM_INT_BITS_BITS 5
|
||||
#define DEFINE_BIGNUMDBLINT typedef unsigned __int64 BignumDblInt
|
||||
|
||||
#elif defined _LP64 && BB_OK(5)
|
||||
|
||||
/*
|
||||
* 32-bit BignumInt, using unsigned long itself as BignumDblInt.
|
||||
*
|
||||
* Only for platforms where long is 64 bits, of course.
|
||||
*/
|
||||
|
||||
typedef unsigned int BignumInt;
|
||||
#define BIGNUM_INT_BITS_BITS 5
|
||||
#define DEFINE_BIGNUMDBLINT typedef unsigned long BignumDblInt
|
||||
|
||||
#elif BB_OK(4)
|
||||
|
||||
/*
|
||||
* 16-bit BignumInt, using unsigned long as BignumDblInt.
|
||||
*
|
||||
* This is the final fallback for real emergencies: C89 guarantees
|
||||
* unsigned short/long to be at least the required sizes, so this
|
||||
* should work on any C implementation at all. But it'll be
|
||||
* noticeably slow, so if you find yourself in this case you
|
||||
* probably want to move heaven and earth to find an alternative!
|
||||
*/
|
||||
|
||||
typedef unsigned short BignumInt;
|
||||
#define BIGNUM_INT_BITS_BITS 4
|
||||
#define DEFINE_BIGNUMDBLINT typedef unsigned long BignumDblInt
|
||||
|
||||
#else
|
||||
|
||||
/* Should only get here if BB_OK(4) evaluated false, i.e. the
|
||||
* command line defined BIGNUM_OVERRIDE to an absurdly small
|
||||
* value. */
|
||||
#error Must define BIGNUM_OVERRIDE to at least 4
|
||||
|
||||
#endif
|
||||
|
||||
#undef BB_OK
|
||||
|
||||
/*
|
||||
* Common code across all branches of that ifdef: define all the
|
||||
* easy constant macros in terms of BIGNUM_INT_BITS_BITS.
|
||||
*/
|
||||
#define BIGNUM_INT_BITS (1 << BIGNUM_INT_BITS_BITS)
|
||||
#define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8)
|
||||
#define BIGNUM_TOP_BIT (((BignumInt)1) << (BIGNUM_INT_BITS-1))
|
||||
#define BIGNUM_INT_MASK (BIGNUM_TOP_BIT | (BIGNUM_TOP_BIT-1))
|
||||
|
||||
/*
|
||||
* Just occasionally, we might need a GET_nnBIT_xSB_FIRST macro to
|
||||
* operate on whatever BignumInt is.
|
||||
*/
|
||||
#if BIGNUM_INT_BITS_BITS == 4
|
||||
#define GET_BIGNUMINT_MSB_FIRST GET_16BIT_MSB_FIRST
|
||||
#define GET_BIGNUMINT_LSB_FIRST GET_16BIT_LSB_FIRST
|
||||
#define PUT_BIGNUMINT_MSB_FIRST PUT_16BIT_MSB_FIRST
|
||||
#define PUT_BIGNUMINT_LSB_FIRST PUT_16BIT_LSB_FIRST
|
||||
#elif BIGNUM_INT_BITS_BITS == 5
|
||||
#define GET_BIGNUMINT_MSB_FIRST GET_32BIT_MSB_FIRST
|
||||
#define GET_BIGNUMINT_LSB_FIRST GET_32BIT_LSB_FIRST
|
||||
#define PUT_BIGNUMINT_MSB_FIRST PUT_32BIT_MSB_FIRST
|
||||
#define PUT_BIGNUMINT_LSB_FIRST PUT_32BIT_LSB_FIRST
|
||||
#elif BIGNUM_INT_BITS_BITS == 6
|
||||
#define GET_BIGNUMINT_MSB_FIRST GET_64BIT_MSB_FIRST
|
||||
#define GET_BIGNUMINT_LSB_FIRST GET_64BIT_LSB_FIRST
|
||||
#define PUT_BIGNUMINT_MSB_FIRST PUT_64BIT_MSB_FIRST
|
||||
#define PUT_BIGNUMINT_LSB_FIRST PUT_64BIT_LSB_FIRST
|
||||
#else
|
||||
#error Ran out of options for GET_BIGNUMINT_xSB_FIRST
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Common code across _most_ branches of the ifdef: define a set of
|
||||
* statement macros in terms of the BignumDblInt type provided. In
|
||||
* this case, we also define BignumCarry to be the same thing as
|
||||
* BignumInt, for simplicity.
|
||||
*/
|
||||
#ifdef DEFINE_BIGNUMDBLINT
|
||||
|
||||
typedef BignumInt BignumCarry;
|
||||
#define BignumADC(ret, retc, a, b, c) do \
|
||||
{ \
|
||||
DEFINE_BIGNUMDBLINT; \
|
||||
BignumDblInt ADC_temp = (BignumInt)(a); \
|
||||
ADC_temp += (BignumInt)(b); \
|
||||
ADC_temp += (c); \
|
||||
(ret) = (BignumInt)ADC_temp; \
|
||||
(retc) = (BignumCarry)(ADC_temp >> BIGNUM_INT_BITS); \
|
||||
} while (0)
|
||||
|
||||
#define BignumMUL(rh, rl, a, b) do \
|
||||
{ \
|
||||
DEFINE_BIGNUMDBLINT; \
|
||||
BignumDblInt MUL_temp = (BignumInt)(a); \
|
||||
MUL_temp *= (BignumInt)(b); \
|
||||
(rh) = (BignumInt)(MUL_temp >> BIGNUM_INT_BITS); \
|
||||
(rl) = (BignumInt)(MUL_temp); \
|
||||
} while (0)
|
||||
|
||||
#define BignumMULADD(rh, rl, a, b, addend) do \
|
||||
{ \
|
||||
DEFINE_BIGNUMDBLINT; \
|
||||
BignumDblInt MUL_temp = (BignumInt)(a); \
|
||||
MUL_temp *= (BignumInt)(b); \
|
||||
MUL_temp += (BignumInt)(addend); \
|
||||
(rh) = (BignumInt)(MUL_temp >> BIGNUM_INT_BITS); \
|
||||
(rl) = (BignumInt)(MUL_temp); \
|
||||
} while (0)
|
||||
|
||||
#define BignumMULADD2(rh, rl, a, b, addend1, addend2) do \
|
||||
{ \
|
||||
DEFINE_BIGNUMDBLINT; \
|
||||
BignumDblInt MUL_temp = (BignumInt)(a); \
|
||||
MUL_temp *= (BignumInt)(b); \
|
||||
MUL_temp += (BignumInt)(addend1); \
|
||||
MUL_temp += (BignumInt)(addend2); \
|
||||
(rh) = (BignumInt)(MUL_temp >> BIGNUM_INT_BITS); \
|
||||
(rl) = (BignumInt)(MUL_temp); \
|
||||
} while (0)
|
||||
|
||||
#endif /* DEFINE_BIGNUMDBLINT */
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Data structures used inside bignum.c.
|
||||
*/
|
||||
|
||||
struct mp_int {
|
||||
size_t nw;
|
||||
BignumInt *w;
|
||||
};
|
||||
|
||||
struct MontyContext {
|
||||
/*
|
||||
* The actual modulus.
|
||||
*/
|
||||
mp_int *m;
|
||||
|
||||
/*
|
||||
* Montgomery multiplication works by selecting a value r > m,
|
||||
* coprime to m, which is really easy to divide by. In binary
|
||||
* arithmetic, that means making it a power of 2; in fact we make
|
||||
* it a whole number of BignumInt.
|
||||
*
|
||||
* We don't store r directly as an mp_int (there's no need). But
|
||||
* its value is 2^rbits; we also store rw = rbits/BIGNUM_INT_BITS
|
||||
* (the corresponding word offset within an mp_int).
|
||||
*
|
||||
* pw is the number of words needed to store an mp_int you're
|
||||
* doing reduction on: it has to be big enough to hold the sum of
|
||||
* an input value up to m^2 plus an extra addend up to m*r.
|
||||
*/
|
||||
size_t rbits, rw, pw;
|
||||
|
||||
/*
|
||||
* The key step in Montgomery reduction requires the inverse of -m
|
||||
* mod r.
|
||||
*/
|
||||
mp_int *minus_minv_mod_r;
|
||||
|
||||
/*
|
||||
* r^1, r^2 and r^3 mod m, which are used for various purposes.
|
||||
*
|
||||
* (Annoyingly, this is one of the rare cases where it would have
|
||||
* been nicer to have a Pascal-style 1-indexed array. I couldn't
|
||||
* _quite_ bring myself to put a gratuitous zero element in here.
|
||||
* So you just have to live with getting r^k by taking the [k-1]th
|
||||
* element of this array.)
|
||||
*/
|
||||
mp_int *powers_of_r_mod_m[3];
|
||||
|
||||
/*
|
||||
* Persistent scratch space from which monty_* functions can
|
||||
* allocate storage for intermediate values.
|
||||
*/
|
||||
mp_int *scratch;
|
||||
};
|
||||
|
||||
/* Functions shared between mpint.c and mpunsafe.c */
|
||||
mp_int *mp_make_sized(size_t nw);
|
||||
@@ -0,0 +1,59 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "misc.h"
|
||||
#include "puttymem.h"
|
||||
|
||||
#include "mpint.h"
|
||||
#include "mpint_i.h"
|
||||
|
||||
/*
|
||||
* This global symbol is also defined in ssh2kex-client.c, to ensure
|
||||
* that these unsafe non-constant-time mp_int functions can't end up
|
||||
* accidentally linked in to any PuTTY tool that actually makes an SSH
|
||||
* client connection.
|
||||
*
|
||||
* (Only _client_ connections, however. Uppity, being a test server
|
||||
* only, is exempt.)
|
||||
*/
|
||||
#ifndef MOD_PERSO
|
||||
const int deliberate_symbol_clash = 12345;
|
||||
#endif
|
||||
|
||||
static size_t mp_unsafe_words_needed(mp_int *x)
|
||||
{
|
||||
size_t words = x->nw;
|
||||
while (words > 1 && !x->w[words-1])
|
||||
words--;
|
||||
return words;
|
||||
}
|
||||
|
||||
mp_int *mp_unsafe_shrink(mp_int *x)
|
||||
{
|
||||
x->nw = mp_unsafe_words_needed(x);
|
||||
/* This potentially leaves some allocated words between the new
|
||||
* and old values of x->nw, which won't be wiped by mp_free now
|
||||
* that x->nw doesn't mention that they exist. But we've just
|
||||
* checked they're all zero, so we don't need to wipe them now
|
||||
* either. */
|
||||
return x;
|
||||
}
|
||||
|
||||
mp_int *mp_unsafe_copy(mp_int *x)
|
||||
{
|
||||
mp_int *copy = mp_make_sized(mp_unsafe_words_needed(x));
|
||||
mp_copy_into(copy, x);
|
||||
return copy;
|
||||
}
|
||||
|
||||
uint32_t mp_unsafe_mod_integer(mp_int *x, uint32_t modulus)
|
||||
{
|
||||
uint64_t accumulator = 0;
|
||||
for (size_t i = mp_max_bytes(x); i-- > 0 ;) {
|
||||
accumulator = 0x100 * accumulator + mp_get_byte(x, i);
|
||||
accumulator %= modulus;
|
||||
}
|
||||
return accumulator;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* mpunsafe.h: functions that deal with mp_ints in ways that are *not*
|
||||
* expected to be constant-time. Used during key generation, in which
|
||||
* constant run time is a lost cause anyway.
|
||||
*
|
||||
* These functions are in a separate header, so that you can easily
|
||||
* check that you're not calling them in the wrong context. They're
|
||||
* also defined in a separate source file, which is only linked in to
|
||||
* the key generation tools. Furthermore, that source file also
|
||||
* defines a global symbol that intentionally conflicts with one
|
||||
* defined in the SSH client code, so that any attempt to put these
|
||||
* functions into the same binary as the live SSH client
|
||||
* implementation will cause a link-time failure. They should only be
|
||||
* linked into PuTTYgen and auxiliary test programs.
|
||||
*
|
||||
* Also, just in case those precautions aren't enough, all the unsafe
|
||||
* functions have 'unsafe' in the name.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_MPINT_UNSAFE_H
|
||||
#define PUTTY_MPINT_UNSAFE_H
|
||||
|
||||
/*
|
||||
* The most obvious unsafe thing you want to do with an mp_int is to
|
||||
* get rid of leading zero words in its representation, so that its
|
||||
* nominal size is as close as possible to its true size, and you
|
||||
* don't waste any time processing it.
|
||||
*
|
||||
* mp_unsafe_shrink performs this operation in place, mutating the
|
||||
* size field of the mp_int it's given. It returns the same pointer it
|
||||
* was given.
|
||||
*
|
||||
* mp_unsafe_copy leaves the original mp_int alone and makes a new one
|
||||
* with the minimal size.
|
||||
*/
|
||||
mp_int *mp_unsafe_shrink(mp_int *m);
|
||||
mp_int *mp_unsafe_copy(mp_int *m);
|
||||
|
||||
/*
|
||||
* Compute the residue of x mod m. This is implemented in the most
|
||||
* obvious way using the C % operator, which won't be constant-time on
|
||||
* many C implementations.
|
||||
*/
|
||||
uint32_t mp_unsafe_mod_integer(mp_int *x, uint32_t m);
|
||||
|
||||
#endif /* PUTTY_MPINT_UNSAFE_H */
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Networking abstraction in PuTTY.
|
||||
*
|
||||
* The way this works is: a back end can choose to open any number
|
||||
* of sockets - including zero, which might be necessary in some.
|
||||
* It can register a bunch of callbacks (most notably for when
|
||||
* data is received) for each socket, and it can call the networking
|
||||
* abstraction to send data without having to worry about blocking.
|
||||
* The stuff behind the abstraction takes care of selects and
|
||||
* nonblocking writes and all that sort of painful gubbins.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_NETWORK_H
|
||||
#define PUTTY_NETWORK_H
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
typedef struct SocketVtable SocketVtable;
|
||||
typedef struct PlugVtable PlugVtable;
|
||||
|
||||
struct Socket {
|
||||
const struct SocketVtable *vt;
|
||||
};
|
||||
|
||||
struct SocketVtable {
|
||||
Plug *(*plug) (Socket *s, Plug *p);
|
||||
/* use a different plug (return the old one) */
|
||||
/* if p is NULL, it doesn't change the plug */
|
||||
/* but it does return the one it's using */
|
||||
void (*close) (Socket *s);
|
||||
size_t (*write) (Socket *s, const void *data, size_t len);
|
||||
size_t (*write_oob) (Socket *s, const void *data, size_t len);
|
||||
void (*write_eof) (Socket *s);
|
||||
void (*set_frozen) (Socket *s, bool is_frozen);
|
||||
/* ignored by tcp, but vital for ssl */
|
||||
const char *(*socket_error) (Socket *s);
|
||||
SocketPeerInfo *(*peer_info) (Socket *s);
|
||||
};
|
||||
|
||||
typedef union { void *p; int i; } accept_ctx_t;
|
||||
typedef Socket *(*accept_fn_t)(accept_ctx_t ctx, Plug *plug);
|
||||
|
||||
struct Plug {
|
||||
const struct PlugVtable *vt;
|
||||
};
|
||||
|
||||
typedef enum PlugLogType {
|
||||
PLUGLOG_CONNECT_TRYING,
|
||||
PLUGLOG_CONNECT_FAILED,
|
||||
PLUGLOG_CONNECT_SUCCESS,
|
||||
PLUGLOG_PROXY_MSG,
|
||||
} PlugLogType;
|
||||
|
||||
struct PlugVtable {
|
||||
void (*log)(Plug *p, PlugLogType type, SockAddr *addr, int port,
|
||||
const char *error_msg, int error_code);
|
||||
/*
|
||||
* Passes the client progress reports on the process of setting
|
||||
* up the connection.
|
||||
*
|
||||
* - PLUGLOG_CONNECT_TRYING means we are about to try to connect
|
||||
* to address `addr' (error_msg and error_code are ignored)
|
||||
*
|
||||
* - PLUGLOG_CONNECT_FAILED means we have failed to connect to
|
||||
* address `addr' (error_msg and error_code are supplied). This
|
||||
* is not a fatal error - we may well have other candidate
|
||||
* addresses to fall back to. When it _is_ fatal, the closing()
|
||||
* function will be called.
|
||||
*
|
||||
* - PLUGLOG_CONNECT_SUCCESS means we have succeeded in
|
||||
* connecting to address `addr'.
|
||||
*
|
||||
* - PLUGLOG_PROXY_MSG means that error_msg contains a line of
|
||||
* logging information from whatever the connection is being
|
||||
* proxied through. This will typically be a wodge of
|
||||
* standard-error output from a local proxy command, so the
|
||||
* receiver should probably prefix it to indicate this.
|
||||
*/
|
||||
void (*closing)
|
||||
(Plug *p, const char *error_msg, int error_code, bool calling_back);
|
||||
/* error_msg is NULL iff it is not an error (ie it closed normally) */
|
||||
/* calling_back != 0 iff there is a Plug function */
|
||||
/* currently running (would cure the fixme in try_send()) */
|
||||
void (*receive) (Plug *p, int urgent, const char *data, size_t len);
|
||||
/*
|
||||
* - urgent==0. `data' points to `len' bytes of perfectly
|
||||
* ordinary data.
|
||||
*
|
||||
* - urgent==1. `data' points to `len' bytes of data,
|
||||
* which were read from before an Urgent pointer.
|
||||
*
|
||||
* - urgent==2. `data' points to `len' bytes of data,
|
||||
* the first of which was the one at the Urgent mark.
|
||||
*/
|
||||
void (*sent) (Plug *p, size_t bufsize);
|
||||
/*
|
||||
* The `sent' function is called when the pending send backlog
|
||||
* on a socket is cleared or partially cleared. The new backlog
|
||||
* size is passed in the `bufsize' parameter.
|
||||
*/
|
||||
int (*accepting)(Plug *p, accept_fn_t constructor, accept_ctx_t ctx);
|
||||
/*
|
||||
* `accepting' is called only on listener-type sockets, and is
|
||||
* passed a constructor function+context that will create a fresh
|
||||
* Socket describing the connection. It returns nonzero if it
|
||||
* doesn't want the connection for some reason, or 0 on success.
|
||||
*/
|
||||
};
|
||||
|
||||
/* proxy indirection layer */
|
||||
/* NB, control of 'addr' is passed via new_connection, which takes
|
||||
* responsibility for freeing it */
|
||||
Socket *new_connection(SockAddr *addr, const char *hostname,
|
||||
int port, bool privport,
|
||||
bool oobinline, bool nodelay, bool keepalive,
|
||||
Plug *plug, Conf *conf);
|
||||
Socket *new_listener(const char *srcaddr, int port, Plug *plug,
|
||||
bool local_host_only, Conf *conf, int addressfamily);
|
||||
SockAddr *name_lookup(const char *host, int port, char **canonicalname,
|
||||
Conf *conf, int addressfamily, LogContext *logctx,
|
||||
const char *lookup_reason_for_logging);
|
||||
|
||||
/* platform-dependent callback from new_connection() */
|
||||
/* (same caveat about addr as new_connection()) */
|
||||
Socket *platform_new_connection(SockAddr *addr, const char *hostname,
|
||||
int port, bool privport,
|
||||
bool oobinline, bool nodelay, bool keepalive,
|
||||
Plug *plug, Conf *conf);
|
||||
|
||||
/* socket functions */
|
||||
|
||||
void sk_init(void); /* called once at program startup */
|
||||
void sk_cleanup(void); /* called just before program exit */
|
||||
|
||||
SockAddr *sk_namelookup(const char *host, char **canonicalname, int address_family);
|
||||
SockAddr *sk_nonamelookup(const char *host);
|
||||
void sk_getaddr(SockAddr *addr, char *buf, int buflen);
|
||||
bool sk_addr_needs_port(SockAddr *addr);
|
||||
bool sk_hostname_is_local(const char *name);
|
||||
bool sk_address_is_local(SockAddr *addr);
|
||||
bool sk_address_is_special_local(SockAddr *addr);
|
||||
int sk_addrtype(SockAddr *addr);
|
||||
void sk_addrcopy(SockAddr *addr, char *buf);
|
||||
void sk_addr_free(SockAddr *addr);
|
||||
/* sk_addr_dup generates another SockAddr which contains the same data
|
||||
* as the original one and can be freed independently. May not actually
|
||||
* physically _duplicate_ it: incrementing a reference count so that
|
||||
* one more free is required before it disappears is an acceptable
|
||||
* implementation. */
|
||||
SockAddr *sk_addr_dup(SockAddr *addr);
|
||||
|
||||
/* NB, control of 'addr' is passed via sk_new, which takes responsibility
|
||||
* for freeing it, as for new_connection() */
|
||||
Socket *sk_new(SockAddr *addr, int port, bool privport, bool oobinline,
|
||||
bool nodelay, bool keepalive, Plug *p);
|
||||
|
||||
Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug,
|
||||
bool local_host_only, int address_family);
|
||||
|
||||
static inline Plug *sk_plug(Socket *s, Plug *p)
|
||||
{ return s->vt->plug(s, p); }
|
||||
static inline void sk_close(Socket *s)
|
||||
{ s->vt->close(s); }
|
||||
static inline size_t sk_write(Socket *s, const void *data, size_t len)
|
||||
{ return s->vt->write(s, data, len); }
|
||||
static inline size_t sk_write_oob(Socket *s, const void *data, size_t len)
|
||||
{ return s->vt->write_oob(s, data, len); }
|
||||
static inline void sk_write_eof(Socket *s)
|
||||
{ s->vt->write_eof(s); }
|
||||
|
||||
static inline void plug_log(
|
||||
Plug *p, int type, SockAddr *addr, int port, const char *msg, int code)
|
||||
{ p->vt->log(p, type, addr, port, msg, code); }
|
||||
static inline void plug_closing(
|
||||
Plug *p, const char *msg, int code, bool calling_back)
|
||||
{ p->vt->closing(p, msg, code, calling_back); }
|
||||
static inline void plug_receive(Plug *p, int urg, const char *data, size_t len)
|
||||
{ p->vt->receive(p, urg, data, len); }
|
||||
static inline void plug_sent (Plug *p, size_t bufsize)
|
||||
{ p->vt->sent(p, bufsize); }
|
||||
static inline int plug_accepting(Plug *p, accept_fn_t cons, accept_ctx_t ctx)
|
||||
{ return p->vt->accepting(p, cons, ctx); }
|
||||
|
||||
/*
|
||||
* Special error values are returned from sk_namelookup and sk_new
|
||||
* if there's a problem. These functions extract an error message,
|
||||
* or return NULL if there's no problem.
|
||||
*/
|
||||
const char *sk_addr_error(SockAddr *addr);
|
||||
static inline const char *sk_socket_error(Socket *s)
|
||||
{ return s->vt->socket_error(s); }
|
||||
|
||||
/*
|
||||
* Set the `frozen' flag on a socket. A frozen socket is one in
|
||||
* which all READABLE notifications are ignored, so that data is
|
||||
* not accepted from the peer until the socket is unfrozen. This
|
||||
* exists for two purposes:
|
||||
*
|
||||
* - Port forwarding: when a local listening port receives a
|
||||
* connection, we do not want to receive data from the new
|
||||
* socket until we have somewhere to send it. Hence, we freeze
|
||||
* the socket until its associated SSH channel is ready; then we
|
||||
* unfreeze it and pending data is delivered.
|
||||
*
|
||||
* - Socket buffering: if an SSH channel (or the whole connection)
|
||||
* backs up or presents a zero window, we must freeze the
|
||||
* associated local socket in order to avoid unbounded buffer
|
||||
* growth.
|
||||
*/
|
||||
static inline void sk_set_frozen(Socket *s, bool is_frozen)
|
||||
{ s->vt->set_frozen(s, is_frozen); }
|
||||
|
||||
/*
|
||||
* Return a structure giving some information about the other end of
|
||||
* the socket. May be NULL, if nothing is available at all. If it is
|
||||
* not NULL, then it is dynamically allocated, and should be freed by
|
||||
* a call to sk_free_peer_info(). See below for the definition.
|
||||
*/
|
||||
static inline SocketPeerInfo *sk_peer_info(Socket *s)
|
||||
{ return s->vt->peer_info(s); }
|
||||
|
||||
/*
|
||||
* The structure returned from sk_peer_info, and a function to free
|
||||
* one (in misc.c).
|
||||
*/
|
||||
struct SocketPeerInfo {
|
||||
int addressfamily;
|
||||
|
||||
/*
|
||||
* Text form of the IPv4 or IPv6 address of the other end of the
|
||||
* socket, if available, in the standard text representation.
|
||||
*/
|
||||
const char *addr_text;
|
||||
|
||||
/*
|
||||
* Binary form of the same address. Filled in if and only if
|
||||
* addr_text is not NULL. You can tell which branch of the union
|
||||
* is used by examining 'addressfamily'.
|
||||
*/
|
||||
union {
|
||||
unsigned char ipv6[16];
|
||||
unsigned char ipv4[4];
|
||||
} addr_bin;
|
||||
|
||||
/*
|
||||
* Remote port number, or -1 if not available.
|
||||
*/
|
||||
int port;
|
||||
|
||||
/*
|
||||
* Free-form text suitable for putting in log messages. For IP
|
||||
* sockets, repeats the address and port information from above.
|
||||
* But it can be completely different, e.g. for Unix-domain
|
||||
* sockets it gives information about the uid, gid and pid of the
|
||||
* connecting process.
|
||||
*/
|
||||
const char *log_text;
|
||||
};
|
||||
void sk_free_peer_info(SocketPeerInfo *pi);
|
||||
|
||||
/*
|
||||
* Simple wrapper on getservbyname(), needed by ssh.c. Returns the
|
||||
* port number, in host byte order (suitable for printf and so on).
|
||||
* Returns 0 on failure. Any platform not supporting getservbyname
|
||||
* can just return 0 - this function is not required to handle
|
||||
* numeric port specifications.
|
||||
*/
|
||||
int net_service_lookup(char *service);
|
||||
|
||||
/*
|
||||
* Look up the local hostname; return value needs freeing.
|
||||
* May return NULL.
|
||||
*/
|
||||
char *get_hostname(void);
|
||||
|
||||
/*
|
||||
* Trivial socket implementation which just stores an error. Found in
|
||||
* errsock.c.
|
||||
*
|
||||
* The consume_string variant takes an already-formatted dynamically
|
||||
* allocated string, and takes over ownership of that string.
|
||||
*/
|
||||
Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...)
|
||||
PRINTF_LIKE(2, 3);
|
||||
Socket *new_error_socket_consume_string(Plug *plug, char *errmsg);
|
||||
|
||||
/*
|
||||
* Trivial plug that does absolutely nothing. Found in nullplug.c.
|
||||
*/
|
||||
extern Plug *const nullplug;
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Functions defined outside the network code, which have to be
|
||||
* declared in this header file rather than the main putty.h because
|
||||
* they use types defined here.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Exports from be_misc.c.
|
||||
*/
|
||||
void backend_socket_log(Seat *seat, LogContext *logctx,
|
||||
PlugLogType type, SockAddr *addr, int port,
|
||||
const char *error_msg, int error_code, Conf *conf,
|
||||
bool session_started);
|
||||
|
||||
typedef struct ProxyStderrBuf {
|
||||
char buf[8192];
|
||||
size_t size;
|
||||
} ProxyStderrBuf;
|
||||
void psb_init(ProxyStderrBuf *psb);
|
||||
void log_proxy_stderr(
|
||||
Plug *plug, ProxyStderrBuf *psb, const void *vdata, size_t len);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* nocmdline.c - stubs in applications which don't do the
|
||||
* standard(ish) PuTTY tools' command-line parsing
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "putty.h"
|
||||
|
||||
/*
|
||||
* Stub version of the function in cmdline.c which provides the
|
||||
* password to SSH authentication by remembering it having been passed
|
||||
* as a command-line option. If we're not doing normal command-line
|
||||
* handling, then there is no such option, so that function always
|
||||
* returns failure.
|
||||
*/
|
||||
int cmdline_get_passwd_input(prompts_t *p)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The main cmdline_process_param function is normally called from
|
||||
* applications' main(). An application linking against this stub
|
||||
* module shouldn't have a main() that calls it in the first place :-)
|
||||
* but it is just occasionally called by other supporting functions,
|
||||
* such as one in uxputty.c which sometimes handles a non-option
|
||||
* argument by making up equivalent options and passing them back to
|
||||
* this function. So we have to provide a link-time stub of this
|
||||
* function, but it had better not end up being called at run time.
|
||||
*/
|
||||
int cmdline_process_param(const char *p, char *value,
|
||||
int need_save, Conf *conf)
|
||||
{
|
||||
unreachable("cmdline_process_param should never be called");
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Routines to refuse to do cryptographic interaction with proxies
|
||||
* in PuTTY. This is a stub implementation of the same interfaces
|
||||
* provided by cproxy.c, for use in PuTTYtel.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "network.h"
|
||||
#include "proxy.h"
|
||||
|
||||
void proxy_socks5_offerencryptedauth(BinarySink *bs)
|
||||
{
|
||||
/* For telnet, don't add any new encrypted authentication routines */
|
||||
}
|
||||
|
||||
int proxy_socks5_handlechap (ProxySocket *p)
|
||||
{
|
||||
|
||||
plug_closing(p->plug, "Proxy error: Trying to handle a SOCKS5 CHAP request"
|
||||
" in telnet-only build",
|
||||
PROXY_ERROR_GENERAL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int proxy_socks5_selectchap(ProxySocket *p)
|
||||
{
|
||||
plug_closing(p->plug, "Proxy error: Trying to handle a SOCKS5 CHAP request"
|
||||
" in telnet-only build",
|
||||
PROXY_ERROR_GENERAL, 0);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Stub definitions of the GSSAPI library list, for Unix pterm and
|
||||
* any other application that needs the symbols defined but has no
|
||||
* use for them.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
const int ngsslibs = 0;
|
||||
const char *const gsslibnames[1] = { "dummy" };
|
||||
const struct keyvalwhere gsslibkeywords[1] = { { "dummy", 0, -1, -1 } };
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Stub implementation of the printing interface for PuTTY, for the
|
||||
* benefit of non-printing terminal applications.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include "putty.h"
|
||||
|
||||
struct printer_job_tag {
|
||||
int dummy;
|
||||
};
|
||||
|
||||
printer_job *printer_start_job(char *printer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void printer_job_data(printer_job *pj, const void *data, size_t len)
|
||||
{
|
||||
}
|
||||
|
||||
void printer_finish_job(printer_job *pj)
|
||||
{
|
||||
}
|
||||
|
||||
printer_enum *printer_start_enum(int *nprinters_ptr)
|
||||
{
|
||||
*nprinters_ptr = 0;
|
||||
return NULL;
|
||||
}
|
||||
char *printer_get_name(printer_enum *pe, int i)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
void printer_finish_enum(printer_enum *pe)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* noproxy.c: an alternative to proxy.c, for use by auxiliary programs
|
||||
* that need to make network connections but don't want to include all
|
||||
* the full-on support for endless network proxies (and its
|
||||
* configuration requirements). Implements the primary APIs of
|
||||
* proxy.c, but maps them straight to the underlying network layer.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
#include "network.h"
|
||||
#include "proxy.h"
|
||||
|
||||
SockAddr *name_lookup(const char *host, int port, char **canonicalname,
|
||||
Conf *conf, int addressfamily, LogContext *logctx,
|
||||
const char *reason)
|
||||
{
|
||||
return sk_namelookup(host, canonicalname, addressfamily);
|
||||
}
|
||||
|
||||
Socket *new_connection(SockAddr *addr, const char *hostname,
|
||||
int port, bool privport,
|
||||
bool oobinline, bool nodelay, bool keepalive,
|
||||
Plug *plug, Conf *conf)
|
||||
{
|
||||
return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug);
|
||||
}
|
||||
|
||||
Socket *new_listener(const char *srcaddr, int port, Plug *plug,
|
||||
bool local_host_only, Conf *conf, int addressfamily)
|
||||
{
|
||||
return sk_newlistener(srcaddr, port, plug, local_host_only, addressfamily);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Stub implementations of RNG functions for applications without an RNG.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
void random_read(void *out, size_t size)
|
||||
{
|
||||
unreachable("Random numbers are not available in this application");
|
||||
}
|
||||
|
||||
void random_save_seed(void)
|
||||
{
|
||||
}
|
||||
|
||||
void random_destroy_seed(void)
|
||||
{
|
||||
}
|
||||
|
||||
void noise_ultralight(NoiseSourceId id, unsigned long data)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Stub implementation of SSH connection-sharing IPC, for any
|
||||
* platform which can't support it at all.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "tree234.h"
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "network.h"
|
||||
|
||||
int platform_ssh_share(const char *name, Conf *conf,
|
||||
Plug *downplug, Plug *upplug, Socket **sock,
|
||||
char **logtext, char **ds_err, char **us_err,
|
||||
bool can_upstream, bool can_downstream)
|
||||
{
|
||||
return SHARE_NONE;
|
||||
}
|
||||
|
||||
void platform_ssh_share_cleanup(const char *name)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Stubs of functions in terminal.c, for use in programs that don't
|
||||
* have a terminal.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
#include "terminal.h"
|
||||
|
||||
void term_nopaste(Terminal *term)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* notiming.c: stub version of timing API.
|
||||
*
|
||||
* Used in any tool which needs a subsystem linked against the
|
||||
* timing API but doesn't want to actually provide timing. For
|
||||
* example, key generation tools need the random number generator,
|
||||
* but they don't want the hassle of calling noise_regular() at
|
||||
* regular intervals - and they don't _need_ it either, since they
|
||||
* have their own rigorous and different means of noise collection.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
unsigned long schedule_timer(int ticks, timer_fn_t fn, void *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void expire_timer_context(void *ctx)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* nullplug.c: provide a null implementation of the Plug vtable which
|
||||
* ignores all calls. Occasionally useful in cases where we want to
|
||||
* make a network connection just to see if it works, but not do
|
||||
* anything with it afterwards except close it again.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
static void nullplug_socket_log(Plug *plug, PlugLogType type, SockAddr *addr,
|
||||
int port, const char *err_msg, int err_code)
|
||||
{
|
||||
}
|
||||
|
||||
static void nullplug_closing(Plug *plug, const char *error_msg, int error_code,
|
||||
bool calling_back)
|
||||
{
|
||||
}
|
||||
|
||||
static void nullplug_receive(
|
||||
Plug *plug, int urgent, const char *data, size_t len)
|
||||
{
|
||||
}
|
||||
|
||||
static void nullplug_sent(Plug *plug, size_t bufsize)
|
||||
{
|
||||
}
|
||||
|
||||
static const PlugVtable nullplug_plugvt = {
|
||||
.log = nullplug_socket_log,
|
||||
.closing = nullplug_closing,
|
||||
.receive = nullplug_receive,
|
||||
.sent = nullplug_sent,
|
||||
};
|
||||
|
||||
static Plug nullplug_plug = { &nullplug_plugvt };
|
||||
|
||||
/*
|
||||
* There's a singleton instance of nullplug, because it's not
|
||||
* interesting enough to worry about making more than one of them.
|
||||
*/
|
||||
Plug *const nullplug = &nullplug_plug;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* pageant.h: header for pageant.c.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef MOD_PERSO
|
||||
NOTIFYICONDATA trayIcone ;
|
||||
int GetAskConfirmationFlag(void) ;
|
||||
int GetShowBalloonOnKeyUsage( void ) ;
|
||||
int ShowBalloonTip( NOTIFYICONDATA tnid, TCHAR title[], TCHAR msg[] ) ;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Upper limit on length of any agent message. Used as a basic sanity
|
||||
* check on messages' length fields, and used by the Windows Pageant
|
||||
* client IPC to decide how large a file mapping to allocate.
|
||||
*/
|
||||
#define AGENT_MAX_MSGLEN 262144
|
||||
|
||||
typedef struct PageantClientVtable PageantClientVtable;
|
||||
typedef struct PageantClient PageantClient;
|
||||
typedef struct PageantClientInfo PageantClientInfo;
|
||||
typedef struct PageantClientRequestId PageantClientRequestId;
|
||||
typedef struct PageantClientDialogId PageantClientDialogId;
|
||||
struct PageantClient {
|
||||
const struct PageantClientVtable *vt;
|
||||
PageantClientInfo *info; /* used by the central Pageant code */
|
||||
|
||||
/* Setting this flag prevents the 'log' vtable entry from ever
|
||||
* being called, so that it's safe to make it NULL. This also
|
||||
* allows optimisations in the core code (it can avoid entire
|
||||
* loops that are only used for logging purposes). So you can also
|
||||
* set it dynamically if you find out at run time that you're not
|
||||
* doing logging. */
|
||||
bool suppress_logging;
|
||||
};
|
||||
struct PageantClientVtable {
|
||||
void (*log)(PageantClient *pc, PageantClientRequestId *reqid,
|
||||
const char *fmt, va_list ap);
|
||||
void (*got_response)(PageantClient *pc, PageantClientRequestId *reqid,
|
||||
ptrlen response);
|
||||
bool (*ask_passphrase)(PageantClient *pc, PageantClientDialogId *dlgid,
|
||||
const char *key_comment);
|
||||
};
|
||||
|
||||
static inline void pageant_client_log_v(
|
||||
PageantClient *pc, PageantClientRequestId *reqid,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
if (!pc->suppress_logging)
|
||||
pc->vt->log(pc, reqid, fmt, ap);
|
||||
}
|
||||
static inline PRINTF_LIKE(3, 4) void pageant_client_log(
|
||||
PageantClient *pc, PageantClientRequestId *reqid, const char *fmt, ...)
|
||||
{
|
||||
if (!pc->suppress_logging) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
pc->vt->log(pc, reqid, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
static inline void pageant_client_got_response(
|
||||
PageantClient *pc, PageantClientRequestId *reqid, ptrlen response)
|
||||
{ pc->vt->got_response(pc, reqid, response); }
|
||||
static inline bool pageant_client_ask_passphrase(
|
||||
PageantClient *pc, PageantClientDialogId *dlgid, const char *comment)
|
||||
{ return pc->vt->ask_passphrase(pc, dlgid, comment); }
|
||||
|
||||
/* PageantClientRequestId is used to match up responses to the agent
|
||||
* requests they refer to. A client may allocate one of these for each
|
||||
* call to pageant_handle_request, (probably as a subfield of some
|
||||
* larger struct on the client side) and expect the same pointer to be
|
||||
* passed back in pageant_client_got_response. */
|
||||
struct PageantClientRequestId { int unused_; };
|
||||
|
||||
/*
|
||||
* Initial setup.
|
||||
*/
|
||||
void pageant_init(void);
|
||||
|
||||
/*
|
||||
* Register and unregister PageantClients. This is necessary so that
|
||||
* when a PageantClient goes away, any unfinished asynchronous
|
||||
* requests can be cleaned up.
|
||||
*
|
||||
* pageant_register_client will fill in pc->id. The client itself
|
||||
* should not touch that field.
|
||||
*/
|
||||
void pageant_register_client(PageantClient *pc);
|
||||
void pageant_unregister_client(PageantClient *pc);
|
||||
|
||||
/*
|
||||
* The main agent function that answers messages.
|
||||
*
|
||||
* Expects a message/length pair as input, minus its initial length
|
||||
* field but still with its type code on the front.
|
||||
*
|
||||
* When a response is ready, the got_response method in the
|
||||
* PageantClient vtable will be passed it in the form of a ptrlen,
|
||||
* again minus its length field.
|
||||
*/
|
||||
void pageant_handle_msg(PageantClient *pc, PageantClientRequestId *reqid,
|
||||
ptrlen msg);
|
||||
|
||||
/*
|
||||
* Send the core Pageant code a response to a passphrase request.
|
||||
*/
|
||||
void pageant_passphrase_request_success(PageantClientDialogId *dlgid,
|
||||
ptrlen passphrase);
|
||||
void pageant_passphrase_request_refused(PageantClientDialogId *dlgid);
|
||||
|
||||
/*
|
||||
* Construct a list of public keys, just as the two LIST_IDENTITIES
|
||||
* requests would have returned them.
|
||||
*/
|
||||
void pageant_make_keylist1(BinarySink *);
|
||||
void pageant_make_keylist2(BinarySink *);
|
||||
|
||||
/*
|
||||
* Accessor functions for Pageant's internal key lists, used by GUI
|
||||
* Pageant, to count the keys, to delete a key, or to re-encrypt a
|
||||
* decrypted-on-demand key (SSH-2 only).
|
||||
*/
|
||||
int pageant_count_ssh1_keys(void);
|
||||
int pageant_count_ssh2_keys(void);
|
||||
bool pageant_delete_nth_ssh1_key(int i);
|
||||
bool pageant_delete_nth_ssh2_key(int i);
|
||||
bool pageant_reencrypt_nth_ssh2_key(int i);
|
||||
void pageant_delete_all(void);
|
||||
void pageant_reencrypt_all(void);
|
||||
|
||||
/*
|
||||
* This callback must be provided by the Pageant front end code.
|
||||
* pageant_handle_msg calls it to indicate that the message it's just
|
||||
* handled has changed the list of keys held by the agent. Front ends
|
||||
* which expose that key list through dedicated UI may need to refresh
|
||||
* that UI's state in this function; other front ends can leave it
|
||||
* empty.
|
||||
*/
|
||||
void keylist_update(void);
|
||||
|
||||
/*
|
||||
* Functions to establish a listening socket speaking the SSH agent
|
||||
* protocol. Call pageant_listener_new() to set up a state; then
|
||||
* create a socket using the returned Plug; then call
|
||||
* pageant_listener_got_socket() to give the listening state its own
|
||||
* socket pointer. Also, provide a logging function later if you want
|
||||
* to.
|
||||
*/
|
||||
typedef struct PageantListenerClientVtable PageantListenerClientVtable;
|
||||
typedef struct PageantListenerClient PageantListenerClient;
|
||||
struct PageantListenerClient {
|
||||
const PageantListenerClientVtable *vt;
|
||||
/* suppress_logging flag works similarly to the one in
|
||||
* PageantClient, but it is only read when a new connection comes
|
||||
* in. So if you do need to change it in mid-run, expect existing
|
||||
* agent connections to still use the old value. */
|
||||
bool suppress_logging;
|
||||
};
|
||||
struct PageantListenerClientVtable {
|
||||
void (*log)(PageantListenerClient *, const char *fmt, va_list ap);
|
||||
bool (*ask_passphrase)(PageantListenerClient *pc,
|
||||
PageantClientDialogId *dlgid,
|
||||
const char *key_comment);
|
||||
};
|
||||
|
||||
static inline void pageant_listener_client_log_v(
|
||||
PageantListenerClient *plc, const char *fmt, va_list ap)
|
||||
{
|
||||
if (!plc->suppress_logging)
|
||||
plc->vt->log(plc, fmt, ap);
|
||||
}
|
||||
static inline PRINTF_LIKE(2, 3) void pageant_listener_client_log(
|
||||
PageantListenerClient *plc, const char *fmt, ...)
|
||||
{
|
||||
if (!plc->suppress_logging) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
plc->vt->log(plc, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
static inline bool pageant_listener_client_ask_passphrase(
|
||||
PageantListenerClient *plc, PageantClientDialogId *dlgid,
|
||||
const char *comment)
|
||||
{ return plc->vt->ask_passphrase(plc, dlgid, comment); }
|
||||
|
||||
struct pageant_listen_state;
|
||||
struct pageant_listen_state *pageant_listener_new(
|
||||
Plug **plug, PageantListenerClient *plc);
|
||||
void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket *);
|
||||
void pageant_listener_free(struct pageant_listen_state *pl);
|
||||
|
||||
/*
|
||||
* Functions to perform specific key actions, either as a client of an
|
||||
* ssh-agent running elsewhere, or directly on the agent state in this
|
||||
* process. (On at least one platform we want to do this in an
|
||||
* agnostic way between the two situations.)
|
||||
*
|
||||
* pageant_add_keyfile() is used to load a private key from a file and
|
||||
* add it to the agent. Initially, you should call it with passphrase
|
||||
* NULL, and it will check if the key is already in the agent, and
|
||||
* whether a passphrase is required. Return values are given in the
|
||||
* enum below. On return, *retstr will either be NULL, or a
|
||||
* dynamically allocated string containing a key comment or an error
|
||||
* message.
|
||||
*
|
||||
* pageant_add_keyfile() also remembers passphrases with which it's
|
||||
* successfully decrypted keys (because if you try to add multiple
|
||||
* keys in one go, you might very well have used the same passphrase
|
||||
* for keys that have the same trust properties). Call
|
||||
* pageant_forget_passphrases() to get rid of them all.
|
||||
*/
|
||||
enum {
|
||||
PAGEANT_ACTION_OK, /* success; no further action needed */
|
||||
PAGEANT_ACTION_FAILURE, /* failure; *retstr is error message */
|
||||
PAGEANT_ACTION_NEED_PP, /* need passphrase: *retstr is key comment */
|
||||
PAGEANT_ACTION_WARNING, /* success but with a warning message;
|
||||
* *retstr is warning message */
|
||||
};
|
||||
int pageant_add_keyfile(Filename *filename, const char *passphrase,
|
||||
char **retstr, bool add_encrypted);
|
||||
void pageant_forget_passphrases(void);
|
||||
|
||||
struct pageant_pubkey {
|
||||
/* Everything needed to identify a public key found by
|
||||
* pageant_enum_keys and pass it back to the agent or other code
|
||||
* later */
|
||||
strbuf *blob;
|
||||
char *comment;
|
||||
int ssh_version;
|
||||
};
|
||||
struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key);
|
||||
void pageant_pubkey_free(struct pageant_pubkey *key);
|
||||
|
||||
typedef void (*pageant_key_enum_fn_t)(void *ctx, char **fingerprints,
|
||||
const char *comment, uint32_t ext_flags,
|
||||
struct pageant_pubkey *key);
|
||||
int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
|
||||
char **retstr);
|
||||
int pageant_delete_key(struct pageant_pubkey *key, char **retstr);
|
||||
int pageant_delete_all_keys(char **retstr);
|
||||
int pageant_reencrypt_key(struct pageant_pubkey *key, char **retstr);
|
||||
int pageant_reencrypt_all_keys(char **retstr);
|
||||
int pageant_sign(struct pageant_pubkey *key, ptrlen message, strbuf *out,
|
||||
uint32_t flags, char **retstr);
|
||||
|
||||
/*
|
||||
* Definitions for agent protocol extensions.
|
||||
*/
|
||||
#define PUTTYEXT(base) base "@putty.projects.tartarus.org"
|
||||
|
||||
#define KNOWN_EXTENSIONS(X) \
|
||||
X(EXT_QUERY, "query") \
|
||||
X(EXT_ADD_PPK, PUTTYEXT("add-ppk")) \
|
||||
X(EXT_REENCRYPT, PUTTYEXT("reencrypt")) \
|
||||
X(EXT_REENCRYPT_ALL, PUTTYEXT("reencrypt-all")) \
|
||||
X(EXT_LIST_EXTENDED, PUTTYEXT("list-extended")) \
|
||||
/* end of list */
|
||||
|
||||
#define LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE 1
|
||||
#define LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY 2
|
||||
@@ -0,0 +1,105 @@
|
||||
/* This file actually defines the GSSAPI function pointers for
|
||||
* functions we plan to import from a GSSAPI library.
|
||||
*/
|
||||
#include "putty.h"
|
||||
|
||||
#ifndef NO_GSSAPI
|
||||
|
||||
#include "pgssapi.h"
|
||||
|
||||
#ifndef NO_LIBDL
|
||||
|
||||
/* Reserved static storage for GSS_oids. Comments are quotes from RFC 2744. */
|
||||
static const gss_OID_desc oids[] = {
|
||||
/* The implementation must reserve static storage for a
|
||||
* gss_OID_desc object containing the value */
|
||||
{10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"},
|
||||
/* corresponding to an object-identifier value of
|
||||
* {iso(1) member-body(2) United States(840) mit(113554)
|
||||
* infosys(1) gssapi(2) generic(1) user_name(1)}. The constant
|
||||
* GSS_C_NT_USER_NAME should be initialized to point
|
||||
* to that gss_OID_desc.
|
||||
|
||||
* The implementation must reserve static storage for a
|
||||
* gss_OID_desc object containing the value */
|
||||
{10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"},
|
||||
/* corresponding to an object-identifier value of
|
||||
* {iso(1) member-body(2) United States(840) mit(113554)
|
||||
* infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
|
||||
* The constant GSS_C_NT_MACHINE_UID_NAME should be
|
||||
* initialized to point to that gss_OID_desc.
|
||||
|
||||
* The implementation must reserve static storage for a
|
||||
* gss_OID_desc object containing the value */
|
||||
{10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"},
|
||||
/* corresponding to an object-identifier value of
|
||||
* {iso(1) member-body(2) United States(840) mit(113554)
|
||||
* infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
|
||||
* The constant GSS_C_NT_STRING_UID_NAME should be
|
||||
* initialized to point to that gss_OID_desc.
|
||||
*
|
||||
* The implementation must reserve static storage for a
|
||||
* gss_OID_desc object containing the value */
|
||||
{6, (void *)"\x2b\x06\x01\x05\x06\x02"},
|
||||
/* corresponding to an object-identifier value of
|
||||
* {iso(1) org(3) dod(6) internet(1) security(5)
|
||||
* nametypes(6) gss-host-based-services(2))}. The constant
|
||||
* GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
|
||||
* to that gss_OID_desc. This is a deprecated OID value, and
|
||||
* implementations wishing to support hostbased-service names
|
||||
* should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
|
||||
* defined below, to identify such names;
|
||||
* GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
|
||||
* for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
|
||||
* parameter, but should not be emitted by GSS-API
|
||||
* implementations
|
||||
*
|
||||
* The implementation must reserve static storage for a
|
||||
* gss_OID_desc object containing the value */
|
||||
{10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"},
|
||||
/* corresponding to an object-identifier value of {iso(1)
|
||||
* member-body(2) Unites States(840) mit(113554) infosys(1)
|
||||
* gssapi(2) generic(1) service_name(4)}. The constant
|
||||
* GSS_C_NT_HOSTBASED_SERVICE should be initialized
|
||||
* to point to that gss_OID_desc.
|
||||
*
|
||||
* The implementation must reserve static storage for a
|
||||
* gss_OID_desc object containing the value */
|
||||
{6, (void *)"\x2b\x06\01\x05\x06\x03"},
|
||||
/* corresponding to an object identifier value of
|
||||
* {1(iso), 3(org), 6(dod), 1(internet), 5(security),
|
||||
* 6(nametypes), 3(gss-anonymous-name)}. The constant
|
||||
* and GSS_C_NT_ANONYMOUS should be initialized to point
|
||||
* to that gss_OID_desc.
|
||||
*
|
||||
* The implementation must reserve static storage for a
|
||||
* gss_OID_desc object containing the value */
|
||||
{6, (void *)"\x2b\x06\x01\x05\x06\x04"},
|
||||
/* corresponding to an object-identifier value of
|
||||
* {1(iso), 3(org), 6(dod), 1(internet), 5(security),
|
||||
* 6(nametypes), 4(gss-api-exported-name)}. The constant
|
||||
* GSS_C_NT_EXPORT_NAME should be initialized to point
|
||||
* to that gss_OID_desc.
|
||||
*/
|
||||
};
|
||||
|
||||
/* Here are the constants which point to the static structure above.
|
||||
*
|
||||
* Constants of the form GSS_C_NT_* are specified by rfc 2744.
|
||||
*/
|
||||
const_gss_OID GSS_C_NT_USER_NAME = oids+0;
|
||||
const_gss_OID GSS_C_NT_MACHINE_UID_NAME = oids+1;
|
||||
const_gss_OID GSS_C_NT_STRING_UID_NAME = oids+2;
|
||||
const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = oids+3;
|
||||
const_gss_OID GSS_C_NT_HOSTBASED_SERVICE = oids+4;
|
||||
const_gss_OID GSS_C_NT_ANONYMOUS = oids+5;
|
||||
const_gss_OID GSS_C_NT_EXPORT_NAME = oids+6;
|
||||
|
||||
#endif /* NO_LIBDL */
|
||||
|
||||
static gss_OID_desc gss_mech_krb5_desc =
|
||||
{ 9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
|
||||
/* iso(1) member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) krb5(2)*/
|
||||
const gss_OID GSS_MECH_KRB5 = &gss_mech_krb5_desc;
|
||||
|
||||
#endif /* NO_GSSAPI */
|
||||
@@ -0,0 +1,333 @@
|
||||
#ifndef PUTTY_PGSSAPI_H
|
||||
#define PUTTY_PGSSAPI_H
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
#ifndef NO_GSSAPI
|
||||
|
||||
/*
|
||||
* On Unix, if we're statically linking against GSSAPI, we leave the
|
||||
* declaration of all this lot to the official header. If we're
|
||||
* dynamically linking, we declare it ourselves, because that avoids
|
||||
* us needing the official header at compile time.
|
||||
*
|
||||
* However, we still need the function pointer types, because even
|
||||
* with statically linked GSSAPI we use the ssh_gss_library wrapper.
|
||||
*/
|
||||
#ifdef STATIC_GSSAPI
|
||||
#include <gssapi/gssapi.h>
|
||||
typedef gss_OID const_gss_OID; /* for our prototypes below */
|
||||
#else /* STATIC_GSSAPI */
|
||||
|
||||
/*******************************************************************************
|
||||
* GSSAPI Definitions, taken from RFC 2744
|
||||
******************************************************************************/
|
||||
|
||||
/* GSSAPI Type Definitions */
|
||||
typedef uint32_t OM_uint32;
|
||||
|
||||
typedef struct gss_OID_desc_struct {
|
||||
OM_uint32 length;
|
||||
void *elements;
|
||||
} gss_OID_desc;
|
||||
typedef const gss_OID_desc *const_gss_OID;
|
||||
typedef gss_OID_desc *gss_OID;
|
||||
|
||||
typedef struct gss_OID_set_desc_struct {
|
||||
size_t count;
|
||||
gss_OID elements;
|
||||
} gss_OID_set_desc;
|
||||
typedef const gss_OID_set_desc *const_gss_OID_set;
|
||||
typedef gss_OID_set_desc *gss_OID_set;
|
||||
|
||||
typedef struct gss_buffer_desc_struct {
|
||||
size_t length;
|
||||
void *value;
|
||||
} gss_buffer_desc, *gss_buffer_t;
|
||||
|
||||
typedef struct gss_channel_bindings_struct {
|
||||
OM_uint32 initiator_addrtype;
|
||||
gss_buffer_desc initiator_address;
|
||||
OM_uint32 acceptor_addrtype;
|
||||
gss_buffer_desc acceptor_address;
|
||||
gss_buffer_desc application_data;
|
||||
} *gss_channel_bindings_t;
|
||||
|
||||
typedef void * gss_ctx_id_t;
|
||||
typedef void * gss_name_t;
|
||||
typedef void * gss_cred_id_t;
|
||||
|
||||
typedef OM_uint32 gss_qop_t;
|
||||
typedef int gss_cred_usage_t;
|
||||
|
||||
/* Flag bits for context-level services. */
|
||||
|
||||
#define GSS_C_DELEG_FLAG 1
|
||||
#define GSS_C_MUTUAL_FLAG 2
|
||||
#define GSS_C_REPLAY_FLAG 4
|
||||
#define GSS_C_SEQUENCE_FLAG 8
|
||||
#define GSS_C_CONF_FLAG 16
|
||||
#define GSS_C_INTEG_FLAG 32
|
||||
#define GSS_C_ANON_FLAG 64
|
||||
#define GSS_C_PROT_READY_FLAG 128
|
||||
#define GSS_C_TRANS_FLAG 256
|
||||
|
||||
/* Credential usage options */
|
||||
#define GSS_C_BOTH 0
|
||||
#define GSS_C_INITIATE 1
|
||||
#define GSS_C_ACCEPT 2
|
||||
|
||||
/*-
|
||||
* RFC 2744 Page 86
|
||||
* Expiration time of 2^32-1 seconds means infinite lifetime for a
|
||||
* credential or security context
|
||||
*/
|
||||
#define GSS_C_INDEFINITE 0xfffffffful
|
||||
|
||||
/* Status code types for gss_display_status */
|
||||
#define GSS_C_GSS_CODE 1
|
||||
#define GSS_C_MECH_CODE 2
|
||||
|
||||
/* The constant definitions for channel-bindings address families */
|
||||
#define GSS_C_AF_UNSPEC 0
|
||||
#define GSS_C_AF_LOCAL 1
|
||||
#define GSS_C_AF_INET 2
|
||||
#define GSS_C_AF_IMPLINK 3
|
||||
#define GSS_C_AF_PUP 4
|
||||
#define GSS_C_AF_CHAOS 5
|
||||
#define GSS_C_AF_NS 6
|
||||
#define GSS_C_AF_NBS 7
|
||||
#define GSS_C_AF_ECMA 8
|
||||
#define GSS_C_AF_DATAKIT 9
|
||||
#define GSS_C_AF_CCITT 10
|
||||
#define GSS_C_AF_SNA 11
|
||||
#define GSS_C_AF_DECnet 12
|
||||
#define GSS_C_AF_DLI 13
|
||||
#define GSS_C_AF_LAT 14
|
||||
#define GSS_C_AF_HYLINK 15
|
||||
#define GSS_C_AF_APPLETALK 16
|
||||
#define GSS_C_AF_BSC 17
|
||||
#define GSS_C_AF_DSS 18
|
||||
#define GSS_C_AF_OSI 19
|
||||
#define GSS_C_AF_X25 21
|
||||
|
||||
#define GSS_C_AF_NULLADDR 255
|
||||
|
||||
/* Various Null values */
|
||||
#define GSS_C_NO_NAME ((gss_name_t) 0)
|
||||
#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
|
||||
#define GSS_C_NO_OID ((gss_OID) 0)
|
||||
#define GSS_C_NO_OID_SET ((gss_OID_set) 0)
|
||||
#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
|
||||
#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
|
||||
#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
|
||||
#define GSS_C_EMPTY_BUFFER {0, NULL}
|
||||
|
||||
/* Major status codes */
|
||||
#define GSS_S_COMPLETE 0
|
||||
|
||||
/* Some "helper" definitions to make the status code macros obvious. */
|
||||
#define GSS_C_CALLING_ERROR_OFFSET 24
|
||||
#define GSS_C_ROUTINE_ERROR_OFFSET 16
|
||||
|
||||
#define GSS_C_SUPPLEMENTARY_OFFSET 0
|
||||
#define GSS_C_CALLING_ERROR_MASK 0377ul
|
||||
#define GSS_C_ROUTINE_ERROR_MASK 0377ul
|
||||
#define GSS_C_SUPPLEMENTARY_MASK 0177777ul
|
||||
|
||||
/*
|
||||
* The macros that test status codes for error conditions.
|
||||
* Note that the GSS_ERROR() macro has changed slightly from
|
||||
* the V1 GSS-API so that it now evaluates its argument
|
||||
* only once.
|
||||
*/
|
||||
#define GSS_CALLING_ERROR(x) \
|
||||
(x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
|
||||
#define GSS_ROUTINE_ERROR(x) \
|
||||
(x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
|
||||
#define GSS_SUPPLEMENTARY_INFO(x) \
|
||||
(x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
|
||||
#define GSS_ERROR(x) \
|
||||
(x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
|
||||
(GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
|
||||
|
||||
/* Now the actual status code definitions */
|
||||
|
||||
/* Calling errors: */
|
||||
#define GSS_S_CALL_INACCESSIBLE_READ \
|
||||
(1ul << GSS_C_CALLING_ERROR_OFFSET)
|
||||
#define GSS_S_CALL_INACCESSIBLE_WRITE \
|
||||
(2ul << GSS_C_CALLING_ERROR_OFFSET)
|
||||
#define GSS_S_CALL_BAD_STRUCTURE \
|
||||
(3ul << GSS_C_CALLING_ERROR_OFFSET)
|
||||
|
||||
/* Routine errors: */
|
||||
#define GSS_S_BAD_MECH (1ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_BAD_NAME (2ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_BAD_NAMETYPE (3ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_BAD_BINDINGS (4ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_BAD_STATUS (5ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_BAD_SIG (6ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_BAD_MIC GSS_S_BAD_SIG
|
||||
#define GSS_S_NO_CRED (7ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_NO_CONTEXT (8ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_DEFECTIVE_TOKEN (9ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_DEFECTIVE_CREDENTIAL (10ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_CREDENTIALS_EXPIRED (11ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_CONTEXT_EXPIRED (12ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_FAILURE (13ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_BAD_QOP (14ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_UNAUTHORIZED (15ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_UNAVAILABLE (16ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_DUPLICATE_ELEMENT (17ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
#define GSS_S_NAME_NOT_MN (18ul << \
|
||||
GSS_C_ROUTINE_ERROR_OFFSET)
|
||||
|
||||
/* Supplementary info bits: */
|
||||
#define GSS_S_CONTINUE_NEEDED \
|
||||
(1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
|
||||
#define GSS_S_DUPLICATE_TOKEN \
|
||||
(1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
|
||||
#define GSS_S_OLD_TOKEN \
|
||||
(1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
|
||||
#define GSS_S_UNSEQ_TOKEN \
|
||||
(1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
|
||||
#define GSS_S_GAP_TOKEN \
|
||||
(1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
|
||||
|
||||
extern const_gss_OID GSS_C_NT_USER_NAME;
|
||||
extern const_gss_OID GSS_C_NT_MACHINE_UID_NAME;
|
||||
extern const_gss_OID GSS_C_NT_STRING_UID_NAME;
|
||||
extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
|
||||
extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE;
|
||||
extern const_gss_OID GSS_C_NT_ANONYMOUS;
|
||||
extern const_gss_OID GSS_C_NT_EXPORT_NAME;
|
||||
|
||||
#endif /* STATIC_GSSAPI */
|
||||
|
||||
extern const gss_OID GSS_MECH_KRB5;
|
||||
|
||||
/* GSSAPI functions we use.
|
||||
* TODO: Replace with all GSSAPI functions from RFC?
|
||||
*/
|
||||
|
||||
/* Calling convention, just in case we need one. */
|
||||
#ifndef GSS_CC
|
||||
#define GSS_CC
|
||||
#endif /*GSS_CC*/
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_release_cred)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
gss_cred_id_t * /*cred_handle*/);
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_init_sec_context)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
const gss_cred_id_t /*initiator_cred_handle*/,
|
||||
gss_ctx_id_t * /*context_handle*/,
|
||||
const gss_name_t /*target_name*/,
|
||||
const gss_OID /*mech_type*/,
|
||||
OM_uint32 /*req_flags*/,
|
||||
OM_uint32 /*time_req*/,
|
||||
const gss_channel_bindings_t /*input_chan_bindings*/,
|
||||
const gss_buffer_t /*input_token*/,
|
||||
gss_OID * /*actual_mech_type*/,
|
||||
gss_buffer_t /*output_token*/,
|
||||
OM_uint32 * /*ret_flags*/,
|
||||
OM_uint32 * /*time_rec*/);
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_delete_sec_context)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
gss_ctx_id_t * /*context_handle*/,
|
||||
gss_buffer_t /*output_token*/);
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_get_mic)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
const gss_ctx_id_t /*context_handle*/,
|
||||
gss_qop_t /*qop_req*/,
|
||||
const gss_buffer_t /*message_buffer*/,
|
||||
gss_buffer_t /*msg_token*/);
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_verify_mic)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
const gss_ctx_id_t /*context_handle*/,
|
||||
const gss_buffer_t /*message_buffer*/,
|
||||
const gss_buffer_t /*msg_token*/,
|
||||
gss_qop_t * /*qop_state*/);
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_display_status)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
OM_uint32 /*status_value*/,
|
||||
int /*status_type*/,
|
||||
const gss_OID /*mech_type*/,
|
||||
OM_uint32 * /*message_context*/,
|
||||
gss_buffer_t /*status_string*/);
|
||||
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_import_name)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
const gss_buffer_t /*input_name_buffer*/,
|
||||
const_gss_OID /*input_name_type*/,
|
||||
gss_name_t * /*output_name*/);
|
||||
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_release_name)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
gss_name_t * /*name*/);
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_release_buffer)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
gss_buffer_t /*buffer*/);
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_acquire_cred)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
const gss_name_t /*desired_name*/,
|
||||
OM_uint32 /*time_req*/,
|
||||
const gss_OID_set /*desired_mechs*/,
|
||||
gss_cred_usage_t /*cred_usage*/,
|
||||
gss_cred_id_t * /*output_cred_handle*/,
|
||||
gss_OID_set * /*actual_mechs*/,
|
||||
OM_uint32 * /*time_rec*/);
|
||||
|
||||
typedef OM_uint32 (GSS_CC *t_gss_inquire_cred_by_mech)
|
||||
(OM_uint32 * /*minor_status*/,
|
||||
const gss_cred_id_t /*cred_handle*/,
|
||||
const gss_OID /*mech_type*/,
|
||||
gss_name_t * /*name*/,
|
||||
OM_uint32 * /*initiator_lifetime*/,
|
||||
OM_uint32 * /*acceptor_lifetime*/,
|
||||
gss_cred_usage_t * /*cred_usage*/);
|
||||
|
||||
struct gssapi_functions {
|
||||
t_gss_delete_sec_context delete_sec_context;
|
||||
t_gss_display_status display_status;
|
||||
t_gss_get_mic get_mic;
|
||||
t_gss_verify_mic verify_mic;
|
||||
t_gss_import_name import_name;
|
||||
t_gss_init_sec_context init_sec_context;
|
||||
t_gss_release_buffer release_buffer;
|
||||
t_gss_release_cred release_cred;
|
||||
t_gss_release_name release_name;
|
||||
t_gss_acquire_cred acquire_cred;
|
||||
t_gss_inquire_cred_by_mech inquire_cred_by_mech;
|
||||
};
|
||||
|
||||
#endif /* NO_GSSAPI */
|
||||
|
||||
#endif /* PUTTY_PGSSAPI_H */
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* pinger.c: centralised module that deals with sending SS_PING
|
||||
* keepalives, to avoid replicating this code in multiple backends.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
struct Pinger {
|
||||
int interval;
|
||||
bool pending;
|
||||
unsigned long when_set, next;
|
||||
Backend *backend;
|
||||
};
|
||||
|
||||
static void pinger_schedule(Pinger *pinger);
|
||||
|
||||
static void pinger_timer(void *ctx, unsigned long now)
|
||||
{
|
||||
Pinger *pinger = (Pinger *)ctx;
|
||||
|
||||
if (pinger->pending && now == pinger->next) {
|
||||
backend_special(pinger->backend, SS_PING, 0);
|
||||
pinger->pending = false;
|
||||
pinger_schedule(pinger);
|
||||
}
|
||||
}
|
||||
|
||||
static void pinger_schedule(Pinger *pinger)
|
||||
{
|
||||
unsigned long next;
|
||||
|
||||
if (!pinger->interval) {
|
||||
pinger->pending = false; /* cancel any pending ping */
|
||||
return;
|
||||
}
|
||||
|
||||
next = schedule_timer(pinger->interval * TICKSPERSEC,
|
||||
pinger_timer, pinger);
|
||||
if (!pinger->pending ||
|
||||
(next - pinger->when_set) < (pinger->next - pinger->when_set)) {
|
||||
pinger->next = next;
|
||||
pinger->when_set = timing_last_clock();
|
||||
pinger->pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
Pinger *pinger_new(Conf *conf, Backend *backend)
|
||||
{
|
||||
Pinger *pinger = snew(Pinger);
|
||||
|
||||
pinger->interval = conf_get_int(conf, CONF_ping_interval);
|
||||
pinger->pending = false;
|
||||
pinger->backend = backend;
|
||||
pinger_schedule(pinger);
|
||||
|
||||
return pinger;
|
||||
}
|
||||
|
||||
void pinger_reconfig(Pinger *pinger, Conf *oldconf, Conf *newconf)
|
||||
{
|
||||
int newinterval = conf_get_int(newconf, CONF_ping_interval);
|
||||
if (conf_get_int(oldconf, CONF_ping_interval) != newinterval) {
|
||||
pinger->interval = newinterval;
|
||||
pinger_schedule(pinger);
|
||||
}
|
||||
}
|
||||
|
||||
void pinger_free(Pinger *pinger)
|
||||
{
|
||||
expire_timer_context(pinger);
|
||||
sfree(pinger);
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
#include <assert.h>
|
||||
#include "ssh.h"
|
||||
#include "sshkeygen.h"
|
||||
#include "mpint.h"
|
||||
#include "mpunsafe.h"
|
||||
#include "tree234.h"
|
||||
|
||||
typedef struct PocklePrimeRecord PocklePrimeRecord;
|
||||
|
||||
struct Pockle {
|
||||
tree234 *tree;
|
||||
|
||||
PocklePrimeRecord **list;
|
||||
size_t nlist, listsize;
|
||||
};
|
||||
|
||||
struct PocklePrimeRecord {
|
||||
mp_int *prime;
|
||||
PocklePrimeRecord **factors;
|
||||
size_t nfactors;
|
||||
mp_int *witness;
|
||||
|
||||
size_t index; /* index in pockle->list */
|
||||
};
|
||||
|
||||
static int ppr_cmp(void *av, void *bv)
|
||||
{
|
||||
PocklePrimeRecord *a = (PocklePrimeRecord *)av;
|
||||
PocklePrimeRecord *b = (PocklePrimeRecord *)bv;
|
||||
return mp_cmp_hs(a->prime, b->prime) - mp_cmp_hs(b->prime, a->prime);
|
||||
}
|
||||
|
||||
static int ppr_find(void *av, void *bv)
|
||||
{
|
||||
mp_int *a = (mp_int *)av;
|
||||
PocklePrimeRecord *b = (PocklePrimeRecord *)bv;
|
||||
return mp_cmp_hs(a, b->prime) - mp_cmp_hs(b->prime, a);
|
||||
}
|
||||
|
||||
Pockle *pockle_new(void)
|
||||
{
|
||||
Pockle *pockle = snew(Pockle);
|
||||
pockle->tree = newtree234(ppr_cmp);
|
||||
pockle->list = NULL;
|
||||
pockle->nlist = pockle->listsize = 0;
|
||||
return pockle;
|
||||
}
|
||||
|
||||
void pockle_free(Pockle *pockle)
|
||||
{
|
||||
pockle_release(pockle, 0);
|
||||
assert(count234(pockle->tree) == 0);
|
||||
freetree234(pockle->tree);
|
||||
sfree(pockle->list);
|
||||
sfree(pockle);
|
||||
}
|
||||
|
||||
static PockleStatus pockle_insert(Pockle *pockle, mp_int *p, mp_int **factors,
|
||||
size_t nfactors, mp_int *w)
|
||||
{
|
||||
PocklePrimeRecord *pr = snew(PocklePrimeRecord);
|
||||
pr->prime = mp_copy(p);
|
||||
|
||||
PocklePrimeRecord *found = add234(pockle->tree, pr);
|
||||
if (pr != found) {
|
||||
/* it was already in there */
|
||||
mp_free(pr->prime);
|
||||
sfree(pr);
|
||||
return POCKLE_OK;
|
||||
}
|
||||
|
||||
if (w) {
|
||||
pr->factors = snewn(nfactors, PocklePrimeRecord *);
|
||||
for (size_t i = 0; i < nfactors; i++) {
|
||||
pr->factors[i] = find234(pockle->tree, factors[i], ppr_find);
|
||||
assert(pr->factors[i]);
|
||||
}
|
||||
pr->nfactors = nfactors;
|
||||
pr->witness = mp_copy(w);
|
||||
} else {
|
||||
pr->factors = NULL;
|
||||
pr->nfactors = 0;
|
||||
pr->witness = NULL;
|
||||
}
|
||||
pr->index = pockle->nlist;
|
||||
|
||||
sgrowarray(pockle->list, pockle->listsize, pockle->nlist);
|
||||
pockle->list[pockle->nlist++] = pr;
|
||||
return POCKLE_OK;
|
||||
}
|
||||
|
||||
size_t pockle_mark(Pockle *pockle)
|
||||
{
|
||||
return pockle->nlist;
|
||||
}
|
||||
|
||||
void pockle_release(Pockle *pockle, size_t mark)
|
||||
{
|
||||
while (pockle->nlist > mark) {
|
||||
PocklePrimeRecord *pr = pockle->list[--pockle->nlist];
|
||||
del234(pockle->tree, pr);
|
||||
mp_free(pr->prime);
|
||||
if (pr->witness)
|
||||
mp_free(pr->witness);
|
||||
sfree(pr->factors);
|
||||
sfree(pr);
|
||||
}
|
||||
}
|
||||
|
||||
PockleStatus pockle_add_small_prime(Pockle *pockle, mp_int *p)
|
||||
{
|
||||
if (mp_hs_integer(p, (1ULL << 32)))
|
||||
return POCKLE_SMALL_PRIME_NOT_SMALL;
|
||||
|
||||
uint32_t val = mp_get_integer(p);
|
||||
|
||||
if (val < 2)
|
||||
return POCKLE_PRIME_SMALLER_THAN_2;
|
||||
|
||||
init_smallprimes();
|
||||
for (size_t i = 0; i < NSMALLPRIMES; i++) {
|
||||
if (val == smallprimes[i])
|
||||
break; /* success */
|
||||
if (val % smallprimes[i] == 0)
|
||||
return POCKLE_SMALL_PRIME_NOT_PRIME;
|
||||
}
|
||||
|
||||
return pockle_insert(pockle, p, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
PockleStatus pockle_add_prime(Pockle *pockle, mp_int *p,
|
||||
mp_int **factors, size_t nfactors,
|
||||
mp_int *witness)
|
||||
{
|
||||
MontyContext *mc = NULL;
|
||||
mp_int *x = NULL, *f = NULL, *w = NULL;
|
||||
PockleStatus status;
|
||||
|
||||
/*
|
||||
* We're going to try to verify that p is prime by using
|
||||
* Pocklington's theorem. The idea is that we're given w such that
|
||||
* w^{p-1} == 1 (mod p) (1)
|
||||
* and for a collection of primes q | p-1,
|
||||
* w^{(p-1)/q} - 1 is coprime to p. (2)
|
||||
*
|
||||
* Suppose r is a prime factor of p itself. Consider the
|
||||
* multiplicative order of w mod r. By (1), r | w^{p-1}-1. But by
|
||||
* (2), r does not divide w^{(p-1)/q}-1. So the order of w mod r
|
||||
* is a factor of p-1, but not a factor of (p-1)/q. Hence, the
|
||||
* largest power of q that divides p-1 must also divide ord w.
|
||||
*
|
||||
* Repeating this reasoning for all q, we find that the product of
|
||||
* all the q (which we'll denote f) must divide ord w, which in
|
||||
* turn divides r-1. So f | r-1 for any r | p.
|
||||
*
|
||||
* In particular, this means f < r. That is, all primes r | p are
|
||||
* bigger than f. So if f > sqrt(p), then we've shown p is prime,
|
||||
* because otherwise it would have to be the product of at least
|
||||
* two factors bigger than its own square root.
|
||||
*
|
||||
* With an extra check, we can also show p to be prime even if
|
||||
* we're only given enough factors to make f > cbrt(p). See below
|
||||
* for that part, when we come to it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Start by checking p > 1. It certainly can't be prime otherwise!
|
||||
* (And since we're going to prove it prime by showing all its
|
||||
* prime factors are large, we do also have to know it _has_ at
|
||||
* least one prime factor for that to tell us anything.)
|
||||
*/
|
||||
if (!mp_hs_integer(p, 2))
|
||||
return POCKLE_PRIME_SMALLER_THAN_2;
|
||||
|
||||
/*
|
||||
* Check that all the factors we've been given really are primes
|
||||
* (in the sense that we already had them in our index). Make the
|
||||
* product f, and check it really does divide p-1.
|
||||
*/
|
||||
x = mp_copy(p);
|
||||
mp_sub_integer_into(x, x, 1);
|
||||
f = mp_from_integer(1);
|
||||
for (size_t i = 0; i < nfactors; i++) {
|
||||
mp_int *q = factors[i];
|
||||
|
||||
if (!find234(pockle->tree, q, ppr_find)) {
|
||||
status = POCKLE_FACTOR_NOT_KNOWN_PRIME;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mp_int *quotient = mp_new(mp_max_bits(x));
|
||||
mp_int *residue = mp_new(mp_max_bits(q));
|
||||
mp_divmod_into(x, q, quotient, residue);
|
||||
|
||||
unsigned exact = mp_eq_integer(residue, 0);
|
||||
mp_free(residue);
|
||||
|
||||
mp_free(x);
|
||||
x = quotient;
|
||||
|
||||
if (!exact) {
|
||||
status = POCKLE_FACTOR_NOT_A_FACTOR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mp_int *tmp = f;
|
||||
f = mp_unsafe_shrink(mp_mul(tmp, q));
|
||||
mp_free(tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that f > cbrt(p).
|
||||
*/
|
||||
mp_int *f2 = mp_mul(f, f);
|
||||
mp_int *f3 = mp_mul(f2, f);
|
||||
bool too_big = mp_cmp_hs(p, f3);
|
||||
mp_free(f3);
|
||||
mp_free(f2);
|
||||
if (too_big) {
|
||||
status = POCKLE_PRODUCT_OF_FACTORS_TOO_SMALL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now do the extra check that allows us to get away with only
|
||||
* having f > cbrt(p) instead of f > sqrt(p).
|
||||
*
|
||||
* If we can show that f | r-1 for any r | p, then we've ruled out
|
||||
* p being a product of _more_ than two primes (because then it
|
||||
* would be the product of at least three things bigger than its
|
||||
* own cube root). But we still have to rule out it being a
|
||||
* product of exactly two.
|
||||
*
|
||||
* Suppose for the sake of contradiction that p is the product of
|
||||
* two prime factors. We know both of those factors would have to
|
||||
* be congruent to 1 mod f. So we'd have to have
|
||||
*
|
||||
* p = (uf+1)(vf+1) = (uv)f^2 + (u+v)f + 1 (3)
|
||||
*
|
||||
* We can't have uv >= f, or else that expression would come to at
|
||||
* least f^3, i.e. it would exceed p. So uv < f. Hence, u,v < f as
|
||||
* well.
|
||||
*
|
||||
* Can we have u+v >= f? If we did, then we could write v >= f-u,
|
||||
* and hence f > uv >= u(f-u). That can be rearranged to show that
|
||||
* u^2 > (u-1)f; decrementing the LHS makes the inequality no
|
||||
* longer necessarily strict, so we have u^2-1 >= (u-1)f, and
|
||||
* dividing off u-1 gives u+1 >= f. But we know u < f, so the only
|
||||
* way this could happen would be if u=f-1, which makes v=1. But
|
||||
* _then_ (3) gives us p = (f-1)f^2 + f^2 + 1 = f^3+1. But that
|
||||
* can't be true if f^3 > p. So we can't have u+v >= f either, by
|
||||
* contradiction.
|
||||
*
|
||||
* After all that, what have we shown? We've shown that we can
|
||||
* write p = (uv)f^2 + (u+v)f + 1, with both uv and u+v strictly
|
||||
* less than f. In other words, if you write down p in base f, it
|
||||
* has exactly three digits, and they are uv, u+v and 1.
|
||||
*
|
||||
* But that means we can _find_ u and v: we know p and f, so we
|
||||
* can just extract those digits of p's base-f representation.
|
||||
* Once we've done so, they give the sum and product of the
|
||||
* potential u,v. And given the sum and product of two numbers,
|
||||
* you can make a quadratic which has those numbers as roots.
|
||||
*
|
||||
* We don't actually have to _solve_ the quadratic: all we have to
|
||||
* do is check if its discriminant is a perfect square. If not,
|
||||
* we'll know that no integers u,v can match this description.
|
||||
*/
|
||||
{
|
||||
/* We already have x = (p-1)/f. So we just need to write x in
|
||||
* the form aF + b, and then we have a=uv and b=u+v. */
|
||||
mp_int *a = mp_new(mp_max_bits(x));
|
||||
mp_int *b = mp_new(mp_max_bits(f));
|
||||
mp_divmod_into(x, f, a, b);
|
||||
assert(!mp_cmp_hs(a, f));
|
||||
assert(!mp_cmp_hs(b, f));
|
||||
|
||||
/* If a=0, then that means p < f^2, so we don't need to do
|
||||
* this check at all: the straightforward Pocklington theorem
|
||||
* is all we need. */
|
||||
if (!mp_eq_integer(a, 0)) {
|
||||
unsigned perfect_square = 0;
|
||||
|
||||
mp_int *bsq = mp_mul(b, b);
|
||||
mp_lshift_fixed_into(a, a, 2);
|
||||
|
||||
if (mp_cmp_hs(bsq, a)) {
|
||||
/* b^2-4a is non-negative, so it might be a square.
|
||||
* Check it. */
|
||||
mp_int *discriminant = mp_sub(bsq, a);
|
||||
mp_int *remainder = mp_new(mp_max_bits(discriminant));
|
||||
mp_int *root = mp_nthroot(discriminant, 2, remainder);
|
||||
perfect_square = mp_eq_integer(remainder, 0);
|
||||
mp_free(discriminant);
|
||||
mp_free(root);
|
||||
mp_free(remainder);
|
||||
}
|
||||
|
||||
mp_free(bsq);
|
||||
|
||||
if (perfect_square) {
|
||||
mp_free(b);
|
||||
mp_free(a);
|
||||
status = POCKLE_DISCRIMINANT_IS_SQUARE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
mp_free(b);
|
||||
mp_free(a);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we've done all the checks that are cheaper than a modpow,
|
||||
* so we've ruled out as many things as possible before having to
|
||||
* do any hard work. But there's nothing for it now: make a
|
||||
* MontyContext.
|
||||
*/
|
||||
mc = monty_new(p);
|
||||
w = monty_import(mc, witness);
|
||||
|
||||
/*
|
||||
* The initial Fermat check: is w^{p-1} itself congruent to 1 mod
|
||||
* p?
|
||||
*/
|
||||
{
|
||||
mp_int *pm1 = mp_copy(p);
|
||||
mp_sub_integer_into(pm1, pm1, 1);
|
||||
mp_int *power = monty_pow(mc, w, pm1);
|
||||
unsigned fermat_pass = mp_cmp_eq(power, monty_identity(mc));
|
||||
mp_free(power);
|
||||
mp_free(pm1);
|
||||
|
||||
if (!fermat_pass) {
|
||||
status = POCKLE_FERMAT_TEST_FAILED;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* And now, for each factor q, is w^{(p-1)/q}-1 coprime to p?
|
||||
*/
|
||||
for (size_t i = 0; i < nfactors; i++) {
|
||||
mp_int *q = factors[i];
|
||||
mp_int *exponent = mp_unsafe_shrink(mp_div(p, q));
|
||||
mp_int *power = monty_pow(mc, w, exponent);
|
||||
mp_int *power_extracted = monty_export(mc, power);
|
||||
mp_sub_integer_into(power_extracted, power_extracted, 1);
|
||||
|
||||
unsigned coprime = mp_coprime(power_extracted, p);
|
||||
if (!coprime) {
|
||||
/*
|
||||
* If w^{(p-1)/q}-1 is not coprime to p, the test has
|
||||
* failed. But it makes a difference why. If the power of
|
||||
* w turned out to be 1, so that we took gcd(1-1,p) =
|
||||
* gcd(0,p) = p, that's like an inconclusive Fermat or M-R
|
||||
* test: it might just mean you picked a witness integer
|
||||
* that wasn't a primitive root. But if the power is any
|
||||
* _other_ value mod p that is not coprime to p, it means
|
||||
* we've detected that the number is *actually not prime*!
|
||||
*/
|
||||
if (mp_eq_integer(power_extracted, 0))
|
||||
status = POCKLE_WITNESS_POWER_IS_1;
|
||||
else
|
||||
status = POCKLE_WITNESS_POWER_NOT_COPRIME;
|
||||
}
|
||||
|
||||
mp_free(exponent);
|
||||
mp_free(power);
|
||||
mp_free(power_extracted);
|
||||
|
||||
if (!coprime)
|
||||
goto out; /* with the status we set up above */
|
||||
}
|
||||
|
||||
/*
|
||||
* Success! p is prime. Insert it into our tree234 of known
|
||||
* primes, so that future calls to this function can cite it in
|
||||
* evidence of larger numbers' primality.
|
||||
*/
|
||||
status = pockle_insert(pockle, p, factors, nfactors, witness);
|
||||
|
||||
out:
|
||||
if (x)
|
||||
mp_free(x);
|
||||
if (f)
|
||||
mp_free(f);
|
||||
if (w)
|
||||
mp_free(w);
|
||||
if (mc)
|
||||
monty_free(mc);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void mp_write_decimal(strbuf *sb, mp_int *x)
|
||||
{
|
||||
char *s = mp_get_decimal(x);
|
||||
ptrlen pl = ptrlen_from_asciz(s);
|
||||
put_datapl(sb, pl);
|
||||
smemclr(s, pl.len);
|
||||
sfree(s);
|
||||
}
|
||||
|
||||
strbuf *pockle_mpu(Pockle *pockle, mp_int *p)
|
||||
{
|
||||
strbuf *sb = strbuf_new_nm();
|
||||
PocklePrimeRecord *pr = find234(pockle->tree, p, ppr_find);
|
||||
assert(pr);
|
||||
|
||||
bool *needed = snewn(pockle->nlist, bool);
|
||||
memset(needed, 0, pockle->nlist * sizeof(bool));
|
||||
needed[pr->index] = true;
|
||||
|
||||
strbuf_catf(sb, "[MPU - Primality Certificate]\nVersion 1.0\nBase 10\n\n"
|
||||
"Proof for:\nN ");
|
||||
mp_write_decimal(sb, p);
|
||||
strbuf_catf(sb, "\n");
|
||||
|
||||
for (size_t index = pockle->nlist; index-- > 0 ;) {
|
||||
if (!needed[index])
|
||||
continue;
|
||||
pr = pockle->list[index];
|
||||
|
||||
if (mp_get_nbits(pr->prime) <= 64) {
|
||||
strbuf_catf(sb, "\nType Small\nN ");
|
||||
mp_write_decimal(sb, pr->prime);
|
||||
strbuf_catf(sb, "\n");
|
||||
} else {
|
||||
assert(pr->witness);
|
||||
strbuf_catf(sb, "\nType BLS5\nN ");
|
||||
mp_write_decimal(sb, pr->prime);
|
||||
strbuf_catf(sb, "\n");
|
||||
for (size_t i = 0; i < pr->nfactors; i++) {
|
||||
strbuf_catf(sb, "Q[%"SIZEu"] ", i+1);
|
||||
mp_write_decimal(sb, pr->factors[i]->prime);
|
||||
assert(pr->factors[i]->index < index);
|
||||
needed[pr->factors[i]->index] = true;
|
||||
strbuf_catf(sb, "\n");
|
||||
}
|
||||
for (size_t i = 0; i < pr->nfactors + 1; i++) {
|
||||
strbuf_catf(sb, "A[%"SIZEu"] ", i);
|
||||
mp_write_decimal(sb, pr->witness);
|
||||
strbuf_catf(sb, "\n");
|
||||
}
|
||||
strbuf_catf(sb, "----\n");
|
||||
}
|
||||
}
|
||||
sfree(needed);
|
||||
|
||||
return sb;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* pproxy.c: dummy implementation of platform_new_connection(), to
|
||||
* be supplanted on any platform which has its own local proxy
|
||||
* method.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
#include "network.h"
|
||||
#include "proxy.h"
|
||||
|
||||
Socket *platform_new_connection(SockAddr *addr, const char *hostname,
|
||||
int port, int privport,
|
||||
int oobinline, int nodelay, int keepalive,
|
||||
Plug *plug, Conf *conf)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
/*
|
||||
* primecandidate.c: implementation of the PrimeCandidateSource
|
||||
* abstraction declared in sshkeygen.h.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "ssh.h"
|
||||
#include "mpint.h"
|
||||
#include "mpunsafe.h"
|
||||
#include "sshkeygen.h"
|
||||
|
||||
struct avoid {
|
||||
unsigned mod, res;
|
||||
};
|
||||
|
||||
struct PrimeCandidateSource {
|
||||
unsigned bits;
|
||||
bool ready, try_sophie_germain;
|
||||
bool one_shot, thrown_away_my_shot;
|
||||
|
||||
/* We'll start by making up a random number strictly less than this ... */
|
||||
mp_int *limit;
|
||||
|
||||
/* ... then we'll multiply by 'factor', and add 'addend'. */
|
||||
mp_int *factor, *addend;
|
||||
|
||||
/* Then we'll try to add a small multiple of 'factor' to it to
|
||||
* avoid it being a multiple of any small prime. Also, for RSA, we
|
||||
* may need to avoid it being _this_ multiple of _this_: */
|
||||
unsigned avoid_residue, avoid_modulus;
|
||||
|
||||
/* Once we're actually running, this will be the complete list of
|
||||
* (modulus, residue) pairs we want to avoid. */
|
||||
struct avoid *avoids;
|
||||
size_t navoids, avoidsize;
|
||||
|
||||
/* List of known primes that our number will be congruent to 1 modulo */
|
||||
mp_int **kps;
|
||||
size_t nkps, kpsize;
|
||||
};
|
||||
|
||||
PrimeCandidateSource *pcs_new_with_firstbits(unsigned bits,
|
||||
unsigned first, unsigned nfirst)
|
||||
{
|
||||
PrimeCandidateSource *s = snew(PrimeCandidateSource);
|
||||
|
||||
assert(first >> (nfirst-1) == 1);
|
||||
|
||||
s->bits = bits;
|
||||
s->ready = false;
|
||||
s->try_sophie_germain = false;
|
||||
s->one_shot = false;
|
||||
s->thrown_away_my_shot = false;
|
||||
|
||||
s->kps = NULL;
|
||||
s->nkps = s->kpsize = 0;
|
||||
|
||||
s->avoids = NULL;
|
||||
s->navoids = s->avoidsize = 0;
|
||||
|
||||
/* Make the number that's the lower limit of our range */
|
||||
mp_int *firstmp = mp_from_integer(first);
|
||||
mp_int *base = mp_lshift_fixed(firstmp, bits - nfirst);
|
||||
mp_free(firstmp);
|
||||
|
||||
/* Set the low bit of that, because all (nontrivial) primes are odd */
|
||||
mp_set_bit(base, 0, 1);
|
||||
|
||||
/* That's our addend. Now initialise factor to 2, to ensure we
|
||||
* only generate odd numbers */
|
||||
s->factor = mp_from_integer(2);
|
||||
s->addend = base;
|
||||
|
||||
/* And that means the limit of our random numbers must be one
|
||||
* factor of two _less_ than the position of the low bit of
|
||||
* 'first', because we'll be multiplying the random number by
|
||||
* 2 immediately afterwards. */
|
||||
s->limit = mp_power_2(bits - nfirst - 1);
|
||||
|
||||
/* avoid_modulus == 0 signals that there's no extra residue to avoid */
|
||||
s->avoid_residue = 1;
|
||||
s->avoid_modulus = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
PrimeCandidateSource *pcs_new(unsigned bits)
|
||||
{
|
||||
return pcs_new_with_firstbits(bits, 1, 1);
|
||||
}
|
||||
|
||||
void pcs_free(PrimeCandidateSource *s)
|
||||
{
|
||||
mp_free(s->limit);
|
||||
mp_free(s->factor);
|
||||
mp_free(s->addend);
|
||||
for (size_t i = 0; i < s->nkps; i++)
|
||||
mp_free(s->kps[i]);
|
||||
sfree(s->avoids);
|
||||
sfree(s->kps);
|
||||
sfree(s);
|
||||
}
|
||||
|
||||
void pcs_try_sophie_germain(PrimeCandidateSource *s)
|
||||
{
|
||||
s->try_sophie_germain = true;
|
||||
}
|
||||
|
||||
void pcs_set_oneshot(PrimeCandidateSource *s)
|
||||
{
|
||||
s->one_shot = true;
|
||||
}
|
||||
|
||||
static void pcs_require_residue_inner(PrimeCandidateSource *s,
|
||||
mp_int *mod, mp_int *res)
|
||||
{
|
||||
/*
|
||||
* We already have a factor and addend. Ensure this one doesn't
|
||||
* contradict it.
|
||||
*/
|
||||
mp_int *gcd = mp_gcd(mod, s->factor);
|
||||
mp_int *test1 = mp_mod(s->addend, gcd);
|
||||
mp_int *test2 = mp_mod(res, gcd);
|
||||
assert(mp_cmp_eq(test1, test2));
|
||||
mp_free(test1);
|
||||
mp_free(test2);
|
||||
|
||||
/*
|
||||
* Reduce our input factor and addend, which are constraints on
|
||||
* the ultimate output number, so that they're constraints on the
|
||||
* initial cofactor we're going to make up.
|
||||
*
|
||||
* If we're generating x and we want to ensure ax+b == r (mod m),
|
||||
* how does that work? We've already checked that b == r modulo g
|
||||
* = gcd(a,m), i.e. r-b is a multiple of g, and so are a and m. So
|
||||
* let's write a=gA, m=gM, (r-b)=gR, and then we can start by
|
||||
* dividing that off:
|
||||
*
|
||||
* ax == r-b (mod m )
|
||||
* => gAx == gR (mod gM)
|
||||
* => Ax == R (mod M)
|
||||
*
|
||||
* Now the moduli A,M are coprime, which makes things easier.
|
||||
*
|
||||
* We're going to need to generate the x in this equation by
|
||||
* generating a new smaller value y, multiplying it by M, and
|
||||
* adding some constant K. So we have x = My + K, and we need to
|
||||
* work out what K will satisfy the above equation. In other
|
||||
* words, we need A(My+K) == R (mod M), and the AMy term vanishes,
|
||||
* so we just need AK == R (mod M). So our congruence is solved by
|
||||
* setting K to be R * A^{-1} mod M.
|
||||
*/
|
||||
mp_int *A = mp_div(s->factor, gcd);
|
||||
mp_int *M = mp_div(mod, gcd);
|
||||
mp_int *Rpre = mp_modsub(res, s->addend, mod);
|
||||
mp_int *R = mp_div(Rpre, gcd);
|
||||
mp_int *Ainv = mp_invert(A, M);
|
||||
mp_int *K = mp_modmul(R, Ainv, M);
|
||||
|
||||
mp_free(gcd);
|
||||
mp_free(Rpre);
|
||||
mp_free(Ainv);
|
||||
mp_free(A);
|
||||
mp_free(R);
|
||||
|
||||
/*
|
||||
* So we know we have to transform our existing (factor, addend)
|
||||
* pair into (factor * M, addend * factor * K). Now we just need
|
||||
* to work out what the limit should be on the random value we're
|
||||
* generating.
|
||||
*
|
||||
* If we need My+K < old_limit, then y < (old_limit-K)/M. But the
|
||||
* RHS is a fraction, so in integers, we need y < ceil of it.
|
||||
*/
|
||||
assert(!mp_cmp_hs(K, s->limit));
|
||||
mp_int *dividend = mp_add(s->limit, M);
|
||||
mp_sub_integer_into(dividend, dividend, 1);
|
||||
mp_sub_into(dividend, dividend, K);
|
||||
mp_free(s->limit);
|
||||
s->limit = mp_div(dividend, M);
|
||||
mp_free(dividend);
|
||||
|
||||
/*
|
||||
* Now just update the real factor and addend, and we're done.
|
||||
*/
|
||||
|
||||
mp_int *addend_old = s->addend;
|
||||
mp_int *tmp = mp_mul(s->factor, K); /* use the _old_ value of factor */
|
||||
s->addend = mp_add(s->addend, tmp);
|
||||
mp_free(tmp);
|
||||
mp_free(addend_old);
|
||||
|
||||
mp_int *factor_old = s->factor;
|
||||
s->factor = mp_mul(s->factor, M);
|
||||
mp_free(factor_old);
|
||||
|
||||
mp_free(M);
|
||||
mp_free(K);
|
||||
s->factor = mp_unsafe_shrink(s->factor);
|
||||
s->addend = mp_unsafe_shrink(s->addend);
|
||||
s->limit = mp_unsafe_shrink(s->limit);
|
||||
}
|
||||
|
||||
void pcs_require_residue(PrimeCandidateSource *s,
|
||||
mp_int *mod, mp_int *res_orig)
|
||||
{
|
||||
/*
|
||||
* Reduce the input residue to its least non-negative value, in
|
||||
* case it was given as a larger equivalent value.
|
||||
*/
|
||||
mp_int *res_reduced = mp_mod(res_orig, mod);
|
||||
pcs_require_residue_inner(s, mod, res_reduced);
|
||||
mp_free(res_reduced);
|
||||
}
|
||||
|
||||
void pcs_require_residue_1(PrimeCandidateSource *s, mp_int *mod)
|
||||
{
|
||||
mp_int *res = mp_from_integer(1);
|
||||
pcs_require_residue(s, mod, res);
|
||||
mp_free(res);
|
||||
}
|
||||
|
||||
void pcs_require_residue_1_mod_prime(PrimeCandidateSource *s, mp_int *mod)
|
||||
{
|
||||
pcs_require_residue_1(s, mod);
|
||||
|
||||
sgrowarray(s->kps, s->kpsize, s->nkps);
|
||||
s->kps[s->nkps++] = mp_copy(mod);
|
||||
}
|
||||
|
||||
void pcs_avoid_residue_small(PrimeCandidateSource *s,
|
||||
unsigned mod, unsigned res)
|
||||
{
|
||||
assert(!s->avoid_modulus); /* can't cope with more than one */
|
||||
s->avoid_modulus = mod;
|
||||
s->avoid_residue = res % mod; /* reduce, just in case */
|
||||
}
|
||||
|
||||
static int avoid_cmp(const void *av, const void *bv)
|
||||
{
|
||||
const struct avoid *a = (const struct avoid *)av;
|
||||
const struct avoid *b = (const struct avoid *)bv;
|
||||
return a->mod < b->mod ? -1 : a->mod > b->mod ? +1 : 0;
|
||||
}
|
||||
|
||||
static uint64_t invert(uint64_t a, uint64_t m)
|
||||
{
|
||||
int64_t v0 = a, i0 = 1;
|
||||
int64_t v1 = m, i1 = 0;
|
||||
while (v0) {
|
||||
int64_t tmp, q = v1 / v0;
|
||||
tmp = v0; v0 = v1 - q*v0; v1 = tmp;
|
||||
tmp = i0; i0 = i1 - q*i0; i1 = tmp;
|
||||
}
|
||||
assert(v1 == 1 || v1 == -1);
|
||||
return i1 * v1;
|
||||
}
|
||||
|
||||
void pcs_ready(PrimeCandidateSource *s)
|
||||
{
|
||||
/*
|
||||
* List all the small (modulus, residue) pairs we want to avoid.
|
||||
*/
|
||||
|
||||
init_smallprimes();
|
||||
|
||||
#define ADD_AVOID(newmod, newres) do { \
|
||||
sgrowarray(s->avoids, s->avoidsize, s->navoids); \
|
||||
s->avoids[s->navoids].mod = (newmod); \
|
||||
s->avoids[s->navoids].res = (newres); \
|
||||
s->navoids++; \
|
||||
} while (0)
|
||||
|
||||
unsigned limit = (mp_hs_integer(s->addend, 65536) ? 65536 :
|
||||
mp_get_integer(s->addend));
|
||||
|
||||
/*
|
||||
* Don't be divisible by any small prime, or at least, any prime
|
||||
* smaller than our output number might actually manage to be. (If
|
||||
* asked to generate a really small prime, it would be
|
||||
* embarrassing to rule out legitimate answers on the grounds that
|
||||
* they were divisible by themselves.)
|
||||
*/
|
||||
for (size_t i = 0; i < NSMALLPRIMES && smallprimes[i] < limit; i++)
|
||||
ADD_AVOID(smallprimes[i], 0);
|
||||
|
||||
if (s->try_sophie_germain) {
|
||||
/*
|
||||
* If we're aiming to generate a Sophie Germain prime (i.e. p
|
||||
* such that 2p+1 is also prime), then we also want to ensure
|
||||
* 2p+1 is not congruent to 0 mod any small prime, because if
|
||||
* it is, we'll waste a lot of time generating a p for which
|
||||
* 2p+1 can't possibly work. So we have to avoid an extra
|
||||
* residue mod each odd q.
|
||||
*
|
||||
* We can simplify: 2p+1 == 0 (mod q)
|
||||
* => 2p == -1 (mod q)
|
||||
* => p == -2^{-1} (mod q)
|
||||
*
|
||||
* There's no need to do Euclid's algorithm to compute those
|
||||
* inverses, because for any odd q, the modular inverse of -2
|
||||
* mod q is just (q-1)/2. (Proof: multiplying it by -2 gives
|
||||
* 1-q, which is congruent to 1 mod q.)
|
||||
*/
|
||||
for (size_t i = 0; i < NSMALLPRIMES && smallprimes[i] < limit; i++)
|
||||
if (smallprimes[i] != 2)
|
||||
ADD_AVOID(smallprimes[i], (smallprimes[i] - 1) / 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, if there's a particular modulus and residue we've been
|
||||
* told to avoid, put it on the list.
|
||||
*/
|
||||
if (s->avoid_modulus)
|
||||
ADD_AVOID(s->avoid_modulus, s->avoid_residue);
|
||||
|
||||
#undef ADD_AVOID
|
||||
|
||||
/*
|
||||
* Sort our to-avoid list by modulus. Partly this is so that we'll
|
||||
* check the smaller moduli first during the live runs, which lets
|
||||
* us spot most failing cases earlier rather than later. Also, it
|
||||
* brings equal moduli together, so that we can reuse the residue
|
||||
* we computed from a previous one.
|
||||
*/
|
||||
qsort(s->avoids, s->navoids, sizeof(*s->avoids), avoid_cmp);
|
||||
|
||||
/*
|
||||
* Next, adjust each of these moduli to take account of our factor
|
||||
* and addend. If we want factor*x+addend to avoid being congruent
|
||||
* to 'res' modulo 'mod', then x itself must avoid being congruent
|
||||
* to (res - addend) * factor^{-1}.
|
||||
*
|
||||
* If factor == 0 modulo mod, then the answer will have a fixed
|
||||
* residue anyway, so we can discard it from our list to test.
|
||||
*/
|
||||
int64_t factor_m = 0, addend_m = 0, last_mod = 0;
|
||||
|
||||
size_t out = 0;
|
||||
for (size_t i = 0; i < s->navoids; i++) {
|
||||
int64_t mod = s->avoids[i].mod, res = s->avoids[i].res;
|
||||
if (mod != last_mod) {
|
||||
last_mod = mod;
|
||||
addend_m = mp_unsafe_mod_integer(s->addend, mod);
|
||||
factor_m = mp_unsafe_mod_integer(s->factor, mod);
|
||||
}
|
||||
|
||||
if (factor_m == 0) {
|
||||
assert(res != addend_m);
|
||||
continue;
|
||||
}
|
||||
|
||||
res = (res - addend_m) * invert(factor_m, mod);
|
||||
res %= mod;
|
||||
if (res < 0)
|
||||
res += mod;
|
||||
|
||||
s->avoids[out].mod = mod;
|
||||
s->avoids[out].res = res;
|
||||
out++;
|
||||
}
|
||||
|
||||
s->navoids = out;
|
||||
|
||||
s->ready = true;
|
||||
}
|
||||
|
||||
mp_int *pcs_generate(PrimeCandidateSource *s)
|
||||
{
|
||||
assert(s->ready);
|
||||
if (s->one_shot) {
|
||||
if (s->thrown_away_my_shot)
|
||||
return NULL;
|
||||
s->thrown_away_my_shot = true;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
mp_int *x = mp_random_upto(s->limit);
|
||||
|
||||
int64_t x_res = 0, last_mod = 0;
|
||||
bool ok = true;
|
||||
|
||||
for (size_t i = 0; i < s->navoids; i++) {
|
||||
int64_t mod = s->avoids[i].mod, avoid_res = s->avoids[i].res;
|
||||
|
||||
if (mod != last_mod) {
|
||||
last_mod = mod;
|
||||
x_res = mp_unsafe_mod_integer(x, mod);
|
||||
}
|
||||
|
||||
if (x_res == avoid_res) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
mp_free(x);
|
||||
continue; /* try a new x */
|
||||
}
|
||||
|
||||
/*
|
||||
* We've found a viable x. Make the final output value.
|
||||
*/
|
||||
mp_int *toret = mp_new(s->bits);
|
||||
mp_mul_into(toret, x, s->factor);
|
||||
mp_add_into(toret, toret, s->addend);
|
||||
mp_free(x);
|
||||
return toret;
|
||||
}
|
||||
}
|
||||
|
||||
void pcs_inspect(PrimeCandidateSource *pcs, mp_int **limit_out,
|
||||
mp_int **factor_out, mp_int **addend_out)
|
||||
{
|
||||
*limit_out = mp_copy(pcs->limit);
|
||||
*factor_out = mp_copy(pcs->factor);
|
||||
*addend_out = mp_copy(pcs->addend);
|
||||
}
|
||||
|
||||
unsigned pcs_get_bits(PrimeCandidateSource *pcs)
|
||||
{
|
||||
return pcs->bits;
|
||||
}
|
||||
|
||||
unsigned pcs_get_bits_remaining(PrimeCandidateSource *pcs)
|
||||
{
|
||||
return mp_get_nbits(pcs->limit);
|
||||
}
|
||||
|
||||
mp_int *pcs_get_upper_bound(PrimeCandidateSource *pcs)
|
||||
{
|
||||
/* Compute (limit-1) * factor + addend */
|
||||
mp_int *tmp = mp_mul(pcs->limit, pcs->factor);
|
||||
mp_int *bound = mp_add(tmp, pcs->addend);
|
||||
mp_free(tmp);
|
||||
mp_sub_into(bound, bound, pcs->factor);
|
||||
return bound;
|
||||
}
|
||||
|
||||
mp_int **pcs_get_known_prime_factors(PrimeCandidateSource *pcs, size_t *nout)
|
||||
{
|
||||
*nout = pcs->nkps;
|
||||
return pcs->kps;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* psftp.h: interface between psftp.c / pscp.c, psftpcommon.c, and
|
||||
* each platform-specific SFTP module.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_PSFTP_H
|
||||
#define PUTTY_PSFTP_H
|
||||
|
||||
/*
|
||||
* psftp_getcwd returns the local current directory. The returned
|
||||
* string must be freed by the caller.
|
||||
*/
|
||||
char *psftp_getcwd(void);
|
||||
|
||||
/*
|
||||
* psftp_lcd changes the local current directory. The return value
|
||||
* is NULL on success, or else an error message which must be freed
|
||||
* by the caller.
|
||||
*/
|
||||
char *psftp_lcd(char *newdir);
|
||||
|
||||
/*
|
||||
* Retrieve file times on a local file. Must return two unsigned
|
||||
* longs in POSIX time_t format.
|
||||
*/
|
||||
void get_file_times(char *filename, unsigned long *mtime,
|
||||
unsigned long *atime);
|
||||
|
||||
/*
|
||||
* One iteration of the PSFTP event loop: wait for network data and
|
||||
* process it, once.
|
||||
*/
|
||||
int ssh_sftp_loop_iteration(void);
|
||||
|
||||
/*
|
||||
* Read a command line for PSFTP from standard input. Caller must
|
||||
* free.
|
||||
*
|
||||
* If `backend_required' is true, should also listen for activity
|
||||
* at the backend (rekeys, clientalives, unexpected closures etc)
|
||||
* and respond as necessary, and if the backend closes it should
|
||||
* treat this as a failure condition. If `backend_required' is
|
||||
* false, a back end is not (intentionally) active at all (e.g.
|
||||
* psftp before an `open' command).
|
||||
*/
|
||||
char *ssh_sftp_get_cmdline(const char *prompt, bool backend_required);
|
||||
|
||||
/*
|
||||
* Platform-specific function called when we're about to make a
|
||||
* network connection.
|
||||
*/
|
||||
void platform_psftp_pre_conn_setup(LogPolicy *lp);
|
||||
|
||||
/*
|
||||
* The main program in psftp.c. Called from main() in the platform-
|
||||
* specific code, after doing any platform-specific initialisation.
|
||||
*/
|
||||
int psftp_main(int argc, char *argv[]);
|
||||
|
||||
/*
|
||||
* These functions are used by PSCP to transmit progress updates
|
||||
* and error information to a GUI window managing it. This will
|
||||
* probably only ever be supported on Windows, so these functions
|
||||
* can safely be stubs on all other platforms.
|
||||
*/
|
||||
void gui_update_stats(const char *name, unsigned long size,
|
||||
int percentage, unsigned long elapsed,
|
||||
unsigned long done, unsigned long eta,
|
||||
unsigned long ratebs);
|
||||
void gui_send_errcount(int list, int errs);
|
||||
void gui_send_char(int is_stderr, int c);
|
||||
void gui_enable(const char *arg);
|
||||
|
||||
/*
|
||||
* It's likely that a given platform's implementation of file
|
||||
* transfer utilities is going to want to do things with them that
|
||||
* aren't present in stdio. Hence we supply an alternative
|
||||
* abstraction for file access functions.
|
||||
*
|
||||
* This abstraction tells you the size and access times when you
|
||||
* open an existing file (platforms may choose the meaning of the
|
||||
* file times if it's not clear; whatever they choose will be what
|
||||
* PSCP sends to the server as mtime and atime), and lets you set
|
||||
* the times when saving a new file.
|
||||
*
|
||||
* On the other hand, the abstraction is pretty simple: it supports
|
||||
* only opening a file and reading it, or creating a file and writing
|
||||
* it. None of this read-and-write, seeking-back-and-forth stuff.
|
||||
*/
|
||||
typedef struct RFile RFile;
|
||||
typedef struct WFile WFile;
|
||||
/* Output params size, perms, mtime and atime can all be NULL if
|
||||
* desired. perms will be -1 if the OS does not support POSIX permissions. */
|
||||
RFile *open_existing_file(const char *name, uint64_t *size,
|
||||
unsigned long *mtime, unsigned long *atime,
|
||||
long *perms);
|
||||
WFile *open_existing_wfile(const char *name, uint64_t *size);
|
||||
/* Returns <0 on error, 0 on eof, or number of bytes read, as usual */
|
||||
int read_from_file(RFile *f, void *buffer, int length);
|
||||
/* Closes and frees the RFile */
|
||||
void close_rfile(RFile *f);
|
||||
WFile *open_new_file(const char *name, long perms);
|
||||
/* Returns <0 on error, 0 on eof, or number of bytes written, as usual */
|
||||
int write_to_file(WFile *f, void *buffer, int length);
|
||||
void set_file_times(WFile *f, unsigned long mtime, unsigned long atime);
|
||||
/* Closes and frees the WFile */
|
||||
void close_wfile(WFile *f);
|
||||
/* Seek offset bytes through file */
|
||||
enum { FROM_START, FROM_CURRENT, FROM_END };
|
||||
int seek_file(WFile *f, uint64_t offset, int whence);
|
||||
/* Get file position */
|
||||
uint64_t get_file_posn(WFile *f);
|
||||
/*
|
||||
* Determine the type of a file: nonexistent, file, directory or
|
||||
* weird. `weird' covers anything else - named pipes, Unix sockets,
|
||||
* device files, fish, badgers, you name it. Things marked `weird'
|
||||
* will be skipped over in recursive file transfers, so the only
|
||||
* real reason for not lumping them in with `nonexistent' is that
|
||||
* it allows a slightly more sane error message.
|
||||
*/
|
||||
enum {
|
||||
FILE_TYPE_NONEXISTENT, FILE_TYPE_FILE, FILE_TYPE_DIRECTORY, FILE_TYPE_WEIRD
|
||||
};
|
||||
int file_type(const char *name);
|
||||
|
||||
/*
|
||||
* Read all the file names out of a directory.
|
||||
*/
|
||||
typedef struct DirHandle DirHandle;
|
||||
DirHandle *open_directory(const char *name, const char **errmsg);
|
||||
/* The string returned from this will need freeing if not NULL */
|
||||
char *read_filename(DirHandle *dir);
|
||||
void close_directory(DirHandle *dir);
|
||||
|
||||
/*
|
||||
* Test a filespec to see whether it's a local wildcard or not.
|
||||
* Return values:
|
||||
*
|
||||
* - WCTYPE_WILDCARD (this is a wildcard).
|
||||
* - WCTYPE_FILENAME (this is a single file name).
|
||||
* - WCTYPE_NONEXISTENT (whichever it was, nothing of that name exists).
|
||||
*
|
||||
* Some platforms may choose not to support local wildcards when
|
||||
* they come from the command line; in this case they simply never
|
||||
* return WCTYPE_WILDCARD, but still test the file's existence.
|
||||
* (However, all platforms will probably want to support wildcards
|
||||
* inside the PSFTP CLI.)
|
||||
*/
|
||||
enum {
|
||||
WCTYPE_NONEXISTENT, WCTYPE_FILENAME, WCTYPE_WILDCARD
|
||||
};
|
||||
int test_wildcard(const char *name, bool cmdline);
|
||||
|
||||
/*
|
||||
* Actually return matching file names for a local wildcard.
|
||||
*/
|
||||
typedef struct WildcardMatcher WildcardMatcher;
|
||||
WildcardMatcher *begin_wildcard_matching(const char *name);
|
||||
/* The string returned from this will need freeing if not NULL */
|
||||
char *wildcard_get_filename(WildcardMatcher *dir);
|
||||
void finish_wildcard_matching(WildcardMatcher *dir);
|
||||
|
||||
/*
|
||||
* Vet a filename returned from the remote host, to ensure it isn't
|
||||
* in some way malicious. The idea is that this function is applied
|
||||
* to filenames returned from FXP_READDIR, which means we can panic
|
||||
* if we see _anything_ resembling a directory separator.
|
||||
*
|
||||
* Returns true if the filename is kosher, false if dangerous.
|
||||
*/
|
||||
bool vet_filename(const char *name);
|
||||
|
||||
/*
|
||||
* Create a directory. Returns true on success, false on error.
|
||||
*/
|
||||
bool create_directory(const char *name);
|
||||
|
||||
/*
|
||||
* Concatenate a directory name and a file name. The way this is
|
||||
* done will depend on the OS.
|
||||
*/
|
||||
char *dir_file_cat(const char *dir, const char *file);
|
||||
|
||||
/*
|
||||
* Return a pointer to the portion of str that comes after the last
|
||||
* path component separator.
|
||||
*
|
||||
* If 'local' is false, path component separators are taken to just be
|
||||
* '/', on the assumption that we're discussing the path syntax on the
|
||||
* server. But if 'local' is true, the separators are whatever the
|
||||
* local OS will treat that way - so that includes '\' and ':' on
|
||||
* Windows.
|
||||
*
|
||||
* This function has the annoying strstr() property of taking a const
|
||||
* char * and returning a char *. You should treat it as if it was a
|
||||
* pair of overloaded functions, one mapping mutable->mutable and the
|
||||
* other const->const :-(
|
||||
*/
|
||||
char *stripslashes(const char *str, bool local);
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* In psftpcommon.c
|
||||
*/
|
||||
|
||||
/*
|
||||
* qsort comparison routine for fxp_name structures. Sorts by real
|
||||
* file name.
|
||||
*/
|
||||
int sftp_name_compare(const void *av, const void *bv);
|
||||
|
||||
/*
|
||||
* Shared code for outputting a directory listing in response to a
|
||||
* stream of name structures from FXP_READDIR operations. Used by
|
||||
* psftp's ls command and pscp -ls.
|
||||
*/
|
||||
struct list_directory_from_sftp_ctx;
|
||||
struct fxp_name; /* in sftp.h */
|
||||
struct list_directory_from_sftp_ctx *list_directory_from_sftp_new(void);
|
||||
void list_directory_from_sftp_feed(struct list_directory_from_sftp_ctx *ctx,
|
||||
struct fxp_name *name);
|
||||
void list_directory_from_sftp_finish(struct list_directory_from_sftp_ctx *ctx);
|
||||
void list_directory_from_sftp_free(struct list_directory_from_sftp_ctx *ctx);
|
||||
/* Callbacks provided by the tool front end */
|
||||
void list_directory_from_sftp_warn_unsorted(void);
|
||||
void list_directory_from_sftp_print(struct fxp_name *name);
|
||||
|
||||
#endif /* PUTTY_PSFTP_H */
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* psftpcommon.c: front-end functionality shared between both file
|
||||
* transfer tools across platforms. (As opposed to sftpcommon.c, which
|
||||
* has *protocol*-level common code.)
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "sftp.h"
|
||||
#include "psftp.h"
|
||||
|
||||
#define MAX_NAMES_MEMORY ((size_t)8 << 20)
|
||||
|
||||
/*
|
||||
* qsort comparison routine for fxp_name structures. Sorts by real
|
||||
* file name.
|
||||
*/
|
||||
int sftp_name_compare(const void *av, const void *bv)
|
||||
{
|
||||
const struct fxp_name *const *a = (const struct fxp_name *const *) av;
|
||||
const struct fxp_name *const *b = (const struct fxp_name *const *) bv;
|
||||
return strcmp((*a)->filename, (*b)->filename);
|
||||
}
|
||||
|
||||
struct list_directory_from_sftp_ctx {
|
||||
size_t nnames, namesize, total_memory;
|
||||
struct fxp_name **names;
|
||||
bool sorting;
|
||||
};
|
||||
|
||||
struct list_directory_from_sftp_ctx *list_directory_from_sftp_new(void)
|
||||
{
|
||||
struct list_directory_from_sftp_ctx *ctx =
|
||||
snew(struct list_directory_from_sftp_ctx);
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->sorting = true;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void list_directory_from_sftp_free(struct list_directory_from_sftp_ctx *ctx)
|
||||
{
|
||||
for (size_t i = 0; i < ctx->nnames; i++)
|
||||
fxp_free_name(ctx->names[i]);
|
||||
sfree(ctx->names);
|
||||
sfree(ctx);
|
||||
}
|
||||
|
||||
void list_directory_from_sftp_feed(struct list_directory_from_sftp_ctx *ctx,
|
||||
struct fxp_name *name)
|
||||
{
|
||||
if (ctx->sorting) {
|
||||
/*
|
||||
* Accumulate these filenames into an array that we'll sort -
|
||||
* unless the array gets _really_ big, in which case, to avoid
|
||||
* consuming all the client's memory, we fall back to
|
||||
* outputting the directory listing unsorted.
|
||||
*/
|
||||
size_t this_name_memory =
|
||||
sizeof(*ctx->names) + sizeof(**ctx->names) +
|
||||
strlen(name->filename) +
|
||||
strlen(name->longname);
|
||||
|
||||
if (MAX_NAMES_MEMORY - ctx->total_memory < this_name_memory) {
|
||||
list_directory_from_sftp_warn_unsorted();
|
||||
|
||||
/* Output all the previously stored names. */
|
||||
for (size_t i = 0; i < ctx->nnames; i++) {
|
||||
list_directory_from_sftp_print(ctx->names[i]);
|
||||
fxp_free_name(ctx->names[i]);
|
||||
}
|
||||
|
||||
/* Don't store further names in that array. */
|
||||
sfree(ctx->names);
|
||||
ctx->names = NULL;
|
||||
ctx->nnames = 0;
|
||||
ctx->namesize = 0;
|
||||
ctx->sorting = false;
|
||||
|
||||
/* And don't forget to output the name passed in this
|
||||
* actual function call. */
|
||||
list_directory_from_sftp_print(name);
|
||||
} else {
|
||||
sgrowarray(ctx->names, ctx->namesize, ctx->nnames);
|
||||
ctx->names[ctx->nnames++] = fxp_dup_name(name);
|
||||
ctx->total_memory += this_name_memory;
|
||||
}
|
||||
} else {
|
||||
list_directory_from_sftp_print(name);
|
||||
}
|
||||
}
|
||||
|
||||
void list_directory_from_sftp_finish(struct list_directory_from_sftp_ctx *ctx)
|
||||
{
|
||||
if (ctx->nnames > 0) {
|
||||
assert(ctx->sorting);
|
||||
qsort(ctx->names, ctx->nnames, sizeof(*ctx->names), sftp_name_compare);
|
||||
for (size_t i = 0; i < ctx->nnames; i++)
|
||||
list_directory_from_sftp_print(ctx->names[i]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* Platform-independent parts of a standalone SOCKS server program
|
||||
* based on the PuTTY SOCKS code.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "misc.h"
|
||||
#include "ssh.h"
|
||||
#include "sshchan.h"
|
||||
#include "psocks.h"
|
||||
|
||||
/*
|
||||
* Possible later TODOs:
|
||||
*
|
||||
* - verbosity setting for log messages
|
||||
*
|
||||
* - could import proxy.c and use name_lookup rather than
|
||||
* sk_namelookup, to allow forwarding via some other proxy type
|
||||
*/
|
||||
|
||||
#define BUFLIMIT 16384
|
||||
|
||||
#define LOGBITS(X) \
|
||||
X(CONNSTATUS) \
|
||||
X(DIALOGUE) \
|
||||
/* end of list */
|
||||
|
||||
#define BITINDEX_ENUM(x) LOG_##x##_bitindex,
|
||||
enum { LOGBITS(BITINDEX_ENUM) };
|
||||
#define BITFLAG_ENUM(x) LOG_##x = 1 << LOG_##x##_bitindex,
|
||||
enum { LOGBITS(BITFLAG_ENUM) };
|
||||
|
||||
typedef struct psocks_connection psocks_connection;
|
||||
|
||||
typedef enum RecordDestination {
|
||||
REC_NONE, REC_FILE, REC_PIPE
|
||||
} RecordDestination;
|
||||
|
||||
struct psocks_state {
|
||||
const PsocksPlatform *platform;
|
||||
int listen_port;
|
||||
bool acceptall;
|
||||
PortFwdManager *portfwdmgr;
|
||||
uint64_t next_conn_index;
|
||||
FILE *logging_fp;
|
||||
unsigned log_flags;
|
||||
RecordDestination rec_dest;
|
||||
char *rec_cmd;
|
||||
strbuf *subcmd;
|
||||
|
||||
ConnectionLayer cl;
|
||||
};
|
||||
|
||||
struct psocks_connection {
|
||||
psocks_state *ps;
|
||||
Channel *chan;
|
||||
char *host, *realhost;
|
||||
int port;
|
||||
SockAddr *addr;
|
||||
Socket *socket;
|
||||
bool connecting, eof_pfmgr_to_socket, eof_socket_to_pfmgr;
|
||||
uint64_t index;
|
||||
PsocksDataSink *rec_sink;
|
||||
|
||||
Plug plug;
|
||||
SshChannel sc;
|
||||
};
|
||||
|
||||
static SshChannel *psocks_lportfwd_open(
|
||||
ConnectionLayer *cl, const char *hostname, int port,
|
||||
const char *description, const SocketPeerInfo *pi, Channel *chan);
|
||||
|
||||
static const ConnectionLayerVtable psocks_clvt = {
|
||||
.lportfwd_open = psocks_lportfwd_open,
|
||||
/* everything else is NULL */
|
||||
};
|
||||
|
||||
static size_t psocks_sc_write(SshChannel *sc, bool is_stderr, const void *,
|
||||
size_t);
|
||||
static void psocks_sc_write_eof(SshChannel *sc);
|
||||
static void psocks_sc_initiate_close(SshChannel *sc, const char *err);
|
||||
static void psocks_sc_unthrottle(SshChannel *sc, size_t bufsize);
|
||||
|
||||
static const SshChannelVtable psocks_scvt = {
|
||||
.write = psocks_sc_write,
|
||||
.write_eof = psocks_sc_write_eof,
|
||||
.initiate_close = psocks_sc_initiate_close,
|
||||
.unthrottle = psocks_sc_unthrottle,
|
||||
/* all the rest are NULL */
|
||||
};
|
||||
|
||||
static void psocks_plug_log(Plug *p, PlugLogType type, SockAddr *addr,
|
||||
int port, const char *error_msg, int error_code);
|
||||
static void psocks_plug_closing(Plug *p, const char *error_msg,
|
||||
int error_code, bool calling_back);
|
||||
static void psocks_plug_receive(Plug *p, int urgent,
|
||||
const char *data, size_t len);
|
||||
static void psocks_plug_sent(Plug *p, size_t bufsize);
|
||||
|
||||
static const PlugVtable psocks_plugvt = {
|
||||
.log = psocks_plug_log,
|
||||
.closing = psocks_plug_closing,
|
||||
.receive = psocks_plug_receive,
|
||||
.sent = psocks_plug_sent,
|
||||
};
|
||||
|
||||
static void psocks_conn_log(psocks_connection *conn, const char *fmt, ...)
|
||||
{
|
||||
if (!conn->ps->logging_fp)
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char *msg = dupvprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(conn->ps->logging_fp, "c#%"PRIu64": %s\n", conn->index, msg);
|
||||
sfree(msg);
|
||||
fflush(conn->ps->logging_fp);
|
||||
}
|
||||
|
||||
static void psocks_conn_log_data(psocks_connection *conn, PsocksDirection dir,
|
||||
const void *vdata, size_t len)
|
||||
{
|
||||
if ((conn->ps->log_flags & LOG_DIALOGUE) && conn->ps->logging_fp) {
|
||||
const char *data = vdata;
|
||||
while (len > 0) {
|
||||
const char *nl = memchr(data, '\n', len);
|
||||
size_t thislen = nl ? (nl+1) - data : len;
|
||||
const char *thisdata = data;
|
||||
data += thislen;
|
||||
len -= thislen;
|
||||
|
||||
static const char *const direction_names[2] = {
|
||||
[UP] = "send", [DN] = "recv" };
|
||||
|
||||
fprintf(conn->ps->logging_fp, "c#%"PRIu64": %s \"", conn->index,
|
||||
direction_names[dir]);
|
||||
write_c_string_literal(conn->ps->logging_fp,
|
||||
make_ptrlen(thisdata, thislen));
|
||||
fprintf(conn->ps->logging_fp, "\"\n");
|
||||
}
|
||||
|
||||
fflush(conn->ps->logging_fp);
|
||||
}
|
||||
|
||||
if (conn->rec_sink)
|
||||
put_data(conn->rec_sink->s[dir], vdata, len);
|
||||
}
|
||||
|
||||
static void psocks_connection_establish(void *vctx);
|
||||
|
||||
static SshChannel *psocks_lportfwd_open(
|
||||
ConnectionLayer *cl, const char *hostname, int port,
|
||||
const char *description, const SocketPeerInfo *pi, Channel *chan)
|
||||
{
|
||||
psocks_state *ps = container_of(cl, psocks_state, cl);
|
||||
psocks_connection *conn = snew(psocks_connection);
|
||||
memset(conn, 0, sizeof(*conn));
|
||||
conn->ps = ps;
|
||||
conn->sc.vt = &psocks_scvt;
|
||||
conn->plug.vt = &psocks_plugvt;
|
||||
conn->chan = chan;
|
||||
conn->host = dupstr(hostname);
|
||||
conn->port = port;
|
||||
conn->index = ps->next_conn_index++;
|
||||
if (conn->ps->log_flags & LOG_CONNSTATUS)
|
||||
psocks_conn_log(conn, "request from %s for %s port %d",
|
||||
pi->log_text, hostname, port);
|
||||
switch (conn->ps->rec_dest) {
|
||||
case REC_FILE:
|
||||
{
|
||||
char *fnames[2];
|
||||
FILE *fp[2];
|
||||
bool ok = true;
|
||||
|
||||
static const char *const direction_names[2] = {
|
||||
[UP] = "sockout", [DN] = "sockin" };
|
||||
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
fnames[i] = dupprintf("%s.%"PRIu64, direction_names[i],
|
||||
conn->index);
|
||||
fp[i] = fopen(fnames[i], "wb");
|
||||
if (!fp[i]) {
|
||||
psocks_conn_log(conn, "cannot log this connection: "
|
||||
"creating file '%s': %s",
|
||||
fnames[i], strerror(errno));
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
if (conn->ps->log_flags & LOG_CONNSTATUS)
|
||||
psocks_conn_log(conn, "logging to '%s' / '%s'",
|
||||
fnames[0], fnames[1]);
|
||||
conn->rec_sink = pds_stdio(fp);
|
||||
} else {
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (fp[i]) {
|
||||
remove(fnames[i]);
|
||||
fclose(fp[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < 2; i++)
|
||||
sfree(fnames[i]);
|
||||
}
|
||||
break;
|
||||
case REC_PIPE:
|
||||
{
|
||||
static const char *const direction_args[2] = {
|
||||
[UP] = "out", [DN] = "in" };
|
||||
char *index_arg = dupprintf("%"PRIu64, conn->index);
|
||||
char *err;
|
||||
conn->rec_sink = conn->ps->platform->open_pipes(
|
||||
conn->ps->rec_cmd, direction_args, index_arg, &err);
|
||||
if (!conn->rec_sink) {
|
||||
psocks_conn_log(conn, "cannot log this connection: "
|
||||
"creating pipes: %s", err);
|
||||
sfree(err);
|
||||
}
|
||||
sfree(index_arg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
queue_toplevel_callback(psocks_connection_establish, conn);
|
||||
return &conn->sc;
|
||||
}
|
||||
|
||||
static void psocks_conn_free(psocks_connection *conn)
|
||||
{
|
||||
if (conn->ps->log_flags & LOG_CONNSTATUS)
|
||||
psocks_conn_log(conn, "closed");
|
||||
|
||||
sfree(conn->host);
|
||||
sfree(conn->realhost);
|
||||
if (conn->socket)
|
||||
sk_close(conn->socket);
|
||||
if (conn->chan)
|
||||
chan_free(conn->chan);
|
||||
if (conn->rec_sink)
|
||||
pds_free(conn->rec_sink);
|
||||
delete_callbacks_for_context(conn);
|
||||
sfree(conn);
|
||||
}
|
||||
|
||||
static void psocks_connection_establish(void *vctx)
|
||||
{
|
||||
psocks_connection *conn = (psocks_connection *)vctx;
|
||||
|
||||
/*
|
||||
* Look up destination host name.
|
||||
*/
|
||||
conn->addr = sk_namelookup(conn->host, &conn->realhost, ADDRTYPE_UNSPEC);
|
||||
|
||||
const char *err = sk_addr_error(conn->addr);
|
||||
if (err) {
|
||||
char *msg = dupprintf("name lookup failed: %s", err);
|
||||
chan_open_failed(conn->chan, msg);
|
||||
sfree(msg);
|
||||
|
||||
psocks_conn_free(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make the connection.
|
||||
*/
|
||||
conn->connecting = true;
|
||||
conn->socket = sk_new(conn->addr, conn->port, false, false, false, false,
|
||||
&conn->plug);
|
||||
}
|
||||
|
||||
static size_t psocks_sc_write(SshChannel *sc, bool is_stderr,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
psocks_connection *conn = container_of(sc, psocks_connection, sc);
|
||||
if (!conn->socket) return 0;
|
||||
|
||||
psocks_conn_log_data(conn, UP, data, len);
|
||||
|
||||
return sk_write(conn->socket, data, len);
|
||||
}
|
||||
|
||||
static void psocks_check_close(void *vctx)
|
||||
{
|
||||
psocks_connection *conn = (psocks_connection *)vctx;
|
||||
if (chan_want_close(conn->chan, conn->eof_pfmgr_to_socket,
|
||||
conn->eof_socket_to_pfmgr))
|
||||
psocks_conn_free(conn);
|
||||
}
|
||||
|
||||
static void psocks_sc_write_eof(SshChannel *sc)
|
||||
{
|
||||
psocks_connection *conn = container_of(sc, psocks_connection, sc);
|
||||
if (!conn->socket) return;
|
||||
sk_write_eof(conn->socket);
|
||||
conn->eof_pfmgr_to_socket = true;
|
||||
|
||||
if (conn->ps->log_flags & LOG_DIALOGUE)
|
||||
psocks_conn_log(conn, "send eof");
|
||||
|
||||
queue_toplevel_callback(psocks_check_close, conn);
|
||||
}
|
||||
|
||||
static void psocks_sc_initiate_close(SshChannel *sc, const char *err)
|
||||
{
|
||||
psocks_connection *conn = container_of(sc, psocks_connection, sc);
|
||||
sk_close(conn->socket);
|
||||
conn->socket = NULL;
|
||||
}
|
||||
|
||||
static void psocks_sc_unthrottle(SshChannel *sc, size_t bufsize)
|
||||
{
|
||||
psocks_connection *conn = container_of(sc, psocks_connection, sc);
|
||||
if (bufsize < BUFLIMIT)
|
||||
sk_set_frozen(conn->socket, false);
|
||||
}
|
||||
|
||||
static void psocks_plug_log(Plug *plug, PlugLogType type, SockAddr *addr,
|
||||
int port, const char *error_msg, int error_code)
|
||||
{
|
||||
psocks_connection *conn = container_of(plug, psocks_connection, plug);
|
||||
char addrbuf[256];
|
||||
|
||||
if (!(conn->ps->log_flags & LOG_CONNSTATUS))
|
||||
return;
|
||||
|
||||
switch (type) {
|
||||
case PLUGLOG_CONNECT_TRYING:
|
||||
sk_getaddr(addr, addrbuf, sizeof(addrbuf));
|
||||
if (sk_addr_needs_port(addr))
|
||||
psocks_conn_log(conn, "trying to connect to %s port %d",
|
||||
addrbuf, port);
|
||||
else
|
||||
psocks_conn_log(conn, "trying to connect to %s", addrbuf);
|
||||
break;
|
||||
case PLUGLOG_CONNECT_FAILED:
|
||||
psocks_conn_log(conn, "connection attempt failed: %s", error_msg);
|
||||
break;
|
||||
case PLUGLOG_CONNECT_SUCCESS:
|
||||
psocks_conn_log(conn, "connection established", error_msg);
|
||||
if (conn->connecting) {
|
||||
chan_open_confirmation(conn->chan);
|
||||
conn->connecting = false;
|
||||
}
|
||||
break;
|
||||
case PLUGLOG_PROXY_MSG:
|
||||
psocks_conn_log(conn, "connection setup: %s", error_msg);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void psocks_plug_closing(Plug *plug, const char *error_msg,
|
||||
int error_code, bool calling_back)
|
||||
{
|
||||
psocks_connection *conn = container_of(plug, psocks_connection, plug);
|
||||
if (conn->connecting) {
|
||||
if (conn->ps->log_flags & LOG_CONNSTATUS)
|
||||
psocks_conn_log(conn, "unable to connect: %s", error_msg);
|
||||
|
||||
chan_open_failed(conn->chan, error_msg);
|
||||
conn->eof_socket_to_pfmgr = true;
|
||||
conn->eof_pfmgr_to_socket = true;
|
||||
conn->connecting = false;
|
||||
} else {
|
||||
if (conn->ps->log_flags & LOG_DIALOGUE)
|
||||
psocks_conn_log(conn, "recv eof");
|
||||
|
||||
chan_send_eof(conn->chan);
|
||||
conn->eof_socket_to_pfmgr = true;
|
||||
}
|
||||
queue_toplevel_callback(psocks_check_close, conn);
|
||||
}
|
||||
|
||||
static void psocks_plug_receive(Plug *plug, int urgent,
|
||||
const char *data, size_t len)
|
||||
{
|
||||
psocks_connection *conn = container_of(plug, psocks_connection, plug);
|
||||
size_t bufsize = chan_send(conn->chan, false, data, len);
|
||||
sk_set_frozen(conn->socket, bufsize > BUFLIMIT);
|
||||
|
||||
psocks_conn_log_data(conn, DN, data, len);
|
||||
}
|
||||
|
||||
static void psocks_plug_sent(Plug *plug, size_t bufsize)
|
||||
{
|
||||
psocks_connection *conn = container_of(plug, psocks_connection, plug);
|
||||
sk_set_frozen(conn->socket, bufsize > BUFLIMIT);
|
||||
}
|
||||
|
||||
psocks_state *psocks_new(const PsocksPlatform *platform)
|
||||
{
|
||||
psocks_state *ps = snew(psocks_state);
|
||||
memset(ps, 0, sizeof(*ps));
|
||||
|
||||
ps->listen_port = 1080;
|
||||
ps->acceptall = false;
|
||||
|
||||
ps->cl.vt = &psocks_clvt;
|
||||
ps->portfwdmgr = portfwdmgr_new(&ps->cl);
|
||||
|
||||
ps->logging_fp = stderr; /* could make this configurable later */
|
||||
ps->log_flags = LOG_CONNSTATUS;
|
||||
ps->rec_dest = REC_NONE;
|
||||
ps->platform = platform;
|
||||
ps->subcmd = strbuf_new();
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
void psocks_free(psocks_state *ps)
|
||||
{
|
||||
portfwdmgr_free(ps->portfwdmgr);
|
||||
strbuf_free(ps->subcmd);
|
||||
sfree(ps->rec_cmd);
|
||||
sfree(ps);
|
||||
}
|
||||
|
||||
void psocks_cmdline(psocks_state *ps, int argc, char **argv)
|
||||
{
|
||||
bool doing_opts = true;
|
||||
bool accumulating_exec_args = false;
|
||||
size_t args_seen = 0;
|
||||
|
||||
while (--argc > 0) {
|
||||
const char *p = *++argv;
|
||||
|
||||
if (doing_opts && p[0] == '-' && p[1]) {
|
||||
if (!strcmp(p, "--")) {
|
||||
doing_opts = false;
|
||||
} else if (!strcmp(p, "-g")) {
|
||||
ps->acceptall = true;
|
||||
} else if (!strcmp(p, "-d")) {
|
||||
ps->log_flags |= LOG_DIALOGUE;
|
||||
} else if (!strcmp(p, "-f")) {
|
||||
ps->rec_dest = REC_FILE;
|
||||
} else if (!strcmp(p, "-p")) {
|
||||
if (!ps->platform->open_pipes) {
|
||||
fprintf(stderr, "psocks: '-p' is not supported on this "
|
||||
"platform\n");
|
||||
exit(1);
|
||||
}
|
||||
if (--argc > 0) {
|
||||
ps->rec_cmd = dupstr(*++argv);
|
||||
} else {
|
||||
fprintf(stderr, "psocks: expected an argument to '-p'\n");
|
||||
exit(1);
|
||||
}
|
||||
ps->rec_dest = REC_PIPE;
|
||||
} else if (!strcmp(p, "--exec")) {
|
||||
if (!ps->platform->start_subcommand) {
|
||||
fprintf(stderr, "psocks: running a subcommand is not "
|
||||
"supported on this platform\n");
|
||||
exit(1);
|
||||
}
|
||||
accumulating_exec_args = true;
|
||||
/* Now consume all further argv words for the
|
||||
* subcommand, even if they look like options */
|
||||
doing_opts = false;
|
||||
} else if (!strcmp(p, "--help")) {
|
||||
printf("usage: psocks [ -d ] [ -f");
|
||||
if (ps->platform->open_pipes)
|
||||
printf(" | -p pipe-cmd");
|
||||
printf(" ] [ -g ] port-number");
|
||||
printf("\n");
|
||||
printf("where: -d log all connection contents to"
|
||||
" standard output\n");
|
||||
printf(" -f record each half-connection to "
|
||||
"a file sockin.N/sockout.N\n");
|
||||
if (ps->platform->open_pipes)
|
||||
printf(" -p pipe-cmd pipe each half-connection"
|
||||
" to 'pipe-cmd [in|out] N'\n");
|
||||
printf(" -g accept connections from anywhere,"
|
||||
" not just localhost\n");
|
||||
if (ps->platform->start_subcommand)
|
||||
printf(" --exec subcmd [args...] run command, and "
|
||||
"terminate when it exits\n");
|
||||
printf(" port-number listen on this port"
|
||||
" (default 1080)\n");
|
||||
printf("also: psocks --help display this help text\n");
|
||||
exit(0);
|
||||
} else {
|
||||
fprintf(stderr, "psocks: unrecognised option '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (accumulating_exec_args) {
|
||||
put_asciz(ps->subcmd, p);
|
||||
} else switch (args_seen++) {
|
||||
case 0:
|
||||
ps->listen_port = atoi(p);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "psocks: unexpected extra argument '%s'\n", p);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void psocks_start(psocks_state *ps)
|
||||
{
|
||||
Conf *conf = conf_new();
|
||||
conf_set_bool(conf, CONF_lport_acceptall, ps->acceptall);
|
||||
char *key = dupprintf("AL%d", ps->listen_port);
|
||||
conf_set_str_str(conf, CONF_portfwd, key, "D");
|
||||
sfree(key);
|
||||
|
||||
portfwdmgr_config(ps->portfwdmgr, conf);
|
||||
|
||||
if (ps->subcmd->len)
|
||||
ps->platform->start_subcommand(ps->subcmd);
|
||||
|
||||
conf_free(conf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some stubs that are needed to link against PuTTY modules.
|
||||
*/
|
||||
|
||||
int verify_host_key(const char *hostname, int port,
|
||||
const char *keytype, const char *key)
|
||||
{
|
||||
unreachable("host keys not handled in this tool");
|
||||
}
|
||||
|
||||
void store_host_key(const char *hostname, int port,
|
||||
const char *keytype, const char *key)
|
||||
{
|
||||
unreachable("host keys not handled in this tool");
|
||||
}
|
||||
|
||||
/*
|
||||
* stdio-targeted PsocksDataSink.
|
||||
*/
|
||||
typedef struct PsocksDataSinkStdio {
|
||||
stdio_sink sink[2];
|
||||
PsocksDataSink pds;
|
||||
} PsocksDataSinkStdio;
|
||||
|
||||
static void stdio_free(PsocksDataSink *pds)
|
||||
{
|
||||
PsocksDataSinkStdio *pdss = container_of(pds, PsocksDataSinkStdio, pds);
|
||||
|
||||
for (size_t i = 0; i < 2; i++)
|
||||
fclose(pdss->sink[i].fp);
|
||||
|
||||
sfree(pdss);
|
||||
}
|
||||
|
||||
PsocksDataSink *pds_stdio(FILE *fp[2])
|
||||
{
|
||||
PsocksDataSinkStdio *pdss = snew(PsocksDataSinkStdio);
|
||||
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
setvbuf(fp[i], NULL, _IONBF, 0);
|
||||
stdio_sink_init(&pdss->sink[i], fp[i]);
|
||||
pdss->pds.s[i] = BinarySink_UPCAST(&pdss->sink[i]);
|
||||
}
|
||||
|
||||
pdss->pds.free = stdio_free;
|
||||
|
||||
return &pdss->pds;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
typedef struct psocks_state psocks_state;
|
||||
|
||||
typedef struct PsocksPlatform PsocksPlatform;
|
||||
typedef struct PsocksDataSink PsocksDataSink;
|
||||
|
||||
/* indices into PsocksDataSink arrays */
|
||||
typedef enum PsocksDirection { UP, DN } PsocksDirection;
|
||||
|
||||
typedef struct PsocksDataSink {
|
||||
void (*free)(PsocksDataSink *);
|
||||
BinarySink *s[2];
|
||||
} PsocksDataSink;
|
||||
static inline void pds_free(PsocksDataSink *pds)
|
||||
{ pds->free(pds); }
|
||||
|
||||
PsocksDataSink *pds_stdio(FILE *fp[2]);
|
||||
|
||||
struct PsocksPlatform {
|
||||
PsocksDataSink *(*open_pipes)(
|
||||
const char *cmd, const char *const *direction_args,
|
||||
const char *index_arg, char **err);
|
||||
void (*start_subcommand)(strbuf *args);
|
||||
};
|
||||
|
||||
psocks_state *psocks_new(const PsocksPlatform *);
|
||||
void psocks_free(psocks_state *ps);
|
||||
void psocks_cmdline(psocks_state *ps, int argc, char **argv);
|
||||
void psocks_start(psocks_state *ps);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* PuTTY memory-handling header.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_PUTTYMEM_H
|
||||
#define PUTTY_PUTTYMEM_H
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
#include <string.h> /* for memcpy() */
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#define smalloc(z) safemalloc(z,1,0)
|
||||
#define snmalloc safemalloc
|
||||
#define srealloc(y,z) saferealloc(y,z,1)
|
||||
#define snrealloc saferealloc
|
||||
#define sfree safefree
|
||||
|
||||
void *safemalloc(size_t factor1, size_t factor2, size_t addend);
|
||||
void *saferealloc(void *, size_t, size_t);
|
||||
void safefree(void *);
|
||||
|
||||
/*
|
||||
* Direct use of smalloc within the code should be avoided where
|
||||
* possible, in favour of these type-casting macros which ensure you
|
||||
* don't mistakenly allocate enough space for one sort of structure
|
||||
* and assign it to a different sort of pointer. sresize also uses
|
||||
* TYPECHECK to verify that the _input_ pointer is a pointer to the
|
||||
* correct type.
|
||||
*/
|
||||
#define snew(type) ((type *)snmalloc(1, sizeof(type), 0))
|
||||
#define snewn(n, type) ((type *)snmalloc((n), sizeof(type), 0))
|
||||
#define sresize(ptr, n, type) TYPECHECK((type *)0 == (ptr), \
|
||||
((type *)snrealloc((ptr), (n), sizeof(type))))
|
||||
|
||||
/*
|
||||
* For cases where you want to allocate a struct plus a subsidiary
|
||||
* data buffer in one step, this macro lets you add a constant to the
|
||||
* amount malloced.
|
||||
*
|
||||
* Since the return value is already cast to the struct type, a
|
||||
* pointer to that many bytes of extra data can be conveniently
|
||||
* obtained by simply adding 1 to the returned pointer!
|
||||
* snew_plus_get_aux is a handy macro that does that and casts the
|
||||
* result to void *, so you can assign it straight to wherever you
|
||||
* wanted it.
|
||||
*/
|
||||
#define snew_plus(type, extra) ((type *)snmalloc(1, sizeof(type), (extra)))
|
||||
#define snew_plus_get_aux(ptr) ((void *)((ptr) + 1))
|
||||
|
||||
/*
|
||||
* Helper macros to deal with the common use case of growing an array.
|
||||
*
|
||||
* The common setup is that 'array' is a pointer to the first element
|
||||
* of a dynamic array of some type, and 'size' represents the current
|
||||
* allocated size of that array (in elements). Both of those macro
|
||||
* parameters are implicitly written back to.
|
||||
*
|
||||
* Then sgrowarray(array, size, n) means: make sure the nth element of
|
||||
* the array exists (i.e. the size is at least n+1). You call that
|
||||
* before writing to the nth element, if you're looping round
|
||||
* appending to the array.
|
||||
*
|
||||
* If you need to grow the array by more than one element, you can
|
||||
* instead call sgrowarrayn(array, size, n, m), which will ensure the
|
||||
* size of the array is at least n+m. (So sgrowarray is just the
|
||||
* special case of that in which m == 1.)
|
||||
*
|
||||
* It's common to call sgrowarrayn with one of n,m equal to the
|
||||
* previous logical length of the array, and the other equal to the
|
||||
* new number of logical entries you want to add, so that n <= size on
|
||||
* entry. But that's not actually a mandatory precondition: the two
|
||||
* length parameters are just arbitrary integers that get added
|
||||
* together with an initial check for overflow, and the semantics are
|
||||
* simply 'make sure the array is big enough to take their sum, no
|
||||
* matter how big it was to start with'.)
|
||||
*
|
||||
* Another occasionally useful idiom is to call sgrowarray with n ==
|
||||
* size, i.e. sgrowarray(array, size, size). That just means: make
|
||||
* array bigger by _some_ amount, I don't particularly mind how much.
|
||||
* You might use that style if you were repeatedly calling an API
|
||||
* function outside your control, which would either fill your buffer
|
||||
* and return success, or else return a 'too big' error without
|
||||
* telling you how much bigger it needed to be.
|
||||
*
|
||||
* The _nm variants of the macro set the 'private' flag in the
|
||||
* underlying function, which forces array resizes to be done by a
|
||||
* manual allocate/copy/free instead of realloc, with careful clearing
|
||||
* of the previous memory block before we free it. This costs
|
||||
* performance, but if the block contains important secrets such as
|
||||
* private keys or passwords, it avoids the risk that a realloc that
|
||||
* moves the memory block might leave a copy of the data visible in
|
||||
* the freed memory at the previous location.
|
||||
*/
|
||||
void *safegrowarray(void *array, size_t *size, size_t eltsize,
|
||||
size_t oldlen, size_t extralen, bool private);
|
||||
|
||||
/* The master macro wrapper, of which all others are special cases */
|
||||
#define sgrowarray_general(array, size, n, m, priv) \
|
||||
((array) = safegrowarray(array, &(size), sizeof(*array), n, m, priv))
|
||||
|
||||
/* The special-case macros that are easier to use in most situations */
|
||||
#define sgrowarrayn( a, s, n, m) sgrowarray_general(a, s, n, m, false)
|
||||
#define sgrowarray( a, s, n ) sgrowarray_general(a, s, n, 1, false)
|
||||
#define sgrowarrayn_nm(a, s, n, m) sgrowarray_general(a, s, n, m, true )
|
||||
#define sgrowarray_nm( a, s, n ) sgrowarray_general(a, s, n, 1, true )
|
||||
|
||||
/*
|
||||
* This function is called by the innermost safemalloc/saferealloc
|
||||
* functions when allocation fails. Usually it's provided by misc.c
|
||||
* which ties it into an application's existing modalfatalbox()
|
||||
* system, but standalone test applications can reimplement it some
|
||||
* other way if they prefer.
|
||||
*/
|
||||
NORETURN void out_of_memory(void);
|
||||
|
||||
#ifdef MINEFIELD
|
||||
/*
|
||||
* Definitions for Minefield, PuTTY's own Windows-specific malloc
|
||||
* debugger in the style of Electric Fence. Implemented in winmisc.c,
|
||||
* and referred to by the main malloc wrappers in memory.c.
|
||||
*/
|
||||
void *minefield_c_malloc(size_t size);
|
||||
void minefield_c_free(void *p);
|
||||
void *minefield_c_realloc(void *p, size_t size);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Find the platform-specific header for this platform.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_PUTTYPS_H
|
||||
#define PUTTY_PUTTYPS_H
|
||||
|
||||
#ifdef _WINDOWS
|
||||
|
||||
#include "winstuff.h"
|
||||
|
||||
#else
|
||||
|
||||
#include "unix.h"
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* "Raw" backend.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
#define RAW_MAX_BACKLOG 4096
|
||||
|
||||
typedef struct Raw Raw;
|
||||
struct Raw {
|
||||
Socket *s;
|
||||
bool closed_on_socket_error;
|
||||
size_t bufsize;
|
||||
Seat *seat;
|
||||
LogContext *logctx;
|
||||
bool sent_console_eof, sent_socket_eof, session_started;
|
||||
|
||||
Conf *conf;
|
||||
|
||||
Plug plug;
|
||||
Backend backend;
|
||||
};
|
||||
|
||||
static void raw_size(Backend *be, int width, int height);
|
||||
|
||||
static void c_write(Raw *raw, const void *buf, size_t len)
|
||||
{
|
||||
size_t backlog = seat_stdout(raw->seat, buf, len);
|
||||
sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG);
|
||||
}
|
||||
|
||||
static void raw_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
|
||||
const char *error_msg, int error_code)
|
||||
{
|
||||
Raw *raw = container_of(plug, Raw, plug);
|
||||
backend_socket_log(raw->seat, raw->logctx, type, addr, port,
|
||||
error_msg, error_code, raw->conf, raw->session_started);
|
||||
}
|
||||
|
||||
static void raw_check_close(Raw *raw)
|
||||
{
|
||||
/*
|
||||
* Called after we send EOF on either the socket or the console.
|
||||
* Its job is to wind up the session once we have sent EOF on both.
|
||||
*/
|
||||
if (raw->sent_console_eof && raw->sent_socket_eof) {
|
||||
if (raw->s) {
|
||||
sk_close(raw->s);
|
||||
raw->s = NULL;
|
||||
seat_notify_remote_exit(raw->seat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void raw_closing(Plug *plug, const char *error_msg, int error_code,
|
||||
bool calling_back)
|
||||
{
|
||||
Raw *raw = container_of(plug, Raw, plug);
|
||||
|
||||
if (error_msg) {
|
||||
/* A socket error has occurred. */
|
||||
if (raw->s) {
|
||||
sk_close(raw->s);
|
||||
raw->s = NULL;
|
||||
raw->closed_on_socket_error = true;
|
||||
seat_notify_remote_exit(raw->seat);
|
||||
}
|
||||
logevent(raw->logctx, error_msg);
|
||||
seat_connection_fatal(raw->seat, "%s", error_msg);
|
||||
} else {
|
||||
/* Otherwise, the remote side closed the connection normally. */
|
||||
if (!raw->sent_console_eof && seat_eof(raw->seat)) {
|
||||
/*
|
||||
* The front end wants us to close the outgoing side of the
|
||||
* connection as soon as we see EOF from the far end.
|
||||
*/
|
||||
if (!raw->sent_socket_eof) {
|
||||
if (raw->s)
|
||||
sk_write_eof(raw->s);
|
||||
raw->sent_socket_eof= true;
|
||||
}
|
||||
}
|
||||
raw->sent_console_eof = true;
|
||||
raw_check_close(raw);
|
||||
}
|
||||
}
|
||||
|
||||
static void raw_receive(Plug *plug, int urgent, const char *data, size_t len)
|
||||
{
|
||||
Raw *raw = container_of(plug, Raw, plug);
|
||||
c_write(raw, data, len);
|
||||
/* We count 'session start', for proxy logging purposes, as being
|
||||
* when data is received from the network and printed. */
|
||||
raw->session_started = true;
|
||||
}
|
||||
|
||||
static void raw_sent(Plug *plug, size_t bufsize)
|
||||
{
|
||||
Raw *raw = container_of(plug, Raw, plug);
|
||||
raw->bufsize = bufsize;
|
||||
}
|
||||
|
||||
static const PlugVtable Raw_plugvt = {
|
||||
.log = raw_log,
|
||||
.closing = raw_closing,
|
||||
.receive = raw_receive,
|
||||
.sent = raw_sent,
|
||||
};
|
||||
|
||||
/*
|
||||
* Called to set up the raw 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 char *raw_init(const BackendVtable *vt, 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;
|
||||
Raw *raw;
|
||||
int addressfamily;
|
||||
char *loghost;
|
||||
|
||||
/* No local authentication phase in this protocol */
|
||||
seat_set_trust_status(seat, false);
|
||||
|
||||
raw = snew(Raw);
|
||||
raw->plug.vt = &Raw_plugvt;
|
||||
raw->backend.vt = vt;
|
||||
raw->s = NULL;
|
||||
raw->closed_on_socket_error = false;
|
||||
*backend_handle = &raw->backend;
|
||||
raw->sent_console_eof = raw->sent_socket_eof = false;
|
||||
raw->bufsize = 0;
|
||||
raw->session_started = false;
|
||||
raw->conf = conf_copy(conf);
|
||||
|
||||
raw->seat = seat;
|
||||
raw->logctx = logctx;
|
||||
|
||||
addressfamily = conf_get_int(conf, CONF_addressfamily);
|
||||
/*
|
||||
* Try to find host.
|
||||
*/
|
||||
addr = name_lookup(host, port, realhost, conf, addressfamily,
|
||||
raw->logctx, "main connection");
|
||||
if ((err = sk_addr_error(addr)) != NULL) {
|
||||
sk_addr_free(addr);
|
||||
return dupstr(err);
|
||||
}
|
||||
|
||||
if (port < 0)
|
||||
port = 23; /* default telnet port */
|
||||
|
||||
/*
|
||||
* Open socket.
|
||||
*/
|
||||
raw->s = new_connection(addr, *realhost, port, false, true, nodelay,
|
||||
keepalive, &raw->plug, conf);
|
||||
if ((err = sk_socket_error(raw->s)) != NULL)
|
||||
return dupstr(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';
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void raw_free(Backend *be)
|
||||
{
|
||||
Raw *raw = container_of(be, Raw, backend);
|
||||
|
||||
if (raw->s)
|
||||
sk_close(raw->s);
|
||||
conf_free(raw->conf);
|
||||
sfree(raw);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stub routine (we don't have any need to reconfigure this backend).
|
||||
*/
|
||||
static void raw_reconfig(Backend *be, Conf *conf)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to send data down the raw connection.
|
||||
*/
|
||||
static size_t raw_send(Backend *be, const char *buf, size_t len)
|
||||
{
|
||||
Raw *raw = container_of(be, Raw, backend);
|
||||
|
||||
if (raw->s == NULL)
|
||||
return 0;
|
||||
|
||||
raw->bufsize = sk_write(raw->s, buf, len);
|
||||
|
||||
return raw->bufsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to query the current socket sendability status.
|
||||
*/
|
||||
static size_t raw_sendbuffer(Backend *be)
|
||||
{
|
||||
Raw *raw = container_of(be, Raw, backend);
|
||||
return raw->bufsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to set the size of the window
|
||||
*/
|
||||
static void raw_size(Backend *be, int width, int height)
|
||||
{
|
||||
/* Do nothing! */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send raw special codes. We only handle outgoing EOF here.
|
||||
*/
|
||||
static void raw_special(Backend *be, SessionSpecialCode code, int arg)
|
||||
{
|
||||
Raw *raw = container_of(be, Raw, backend);
|
||||
if (code == SS_EOF && raw->s) {
|
||||
sk_write_eof(raw->s);
|
||||
raw->sent_socket_eof= true;
|
||||
raw_check_close(raw);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a list of the special codes that make sense in this
|
||||
* protocol.
|
||||
*/
|
||||
static const SessionSpecial *raw_get_specials(Backend *be)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool raw_connected(Backend *be)
|
||||
{
|
||||
Raw *raw = container_of(be, Raw, backend);
|
||||
return raw->s != NULL;
|
||||
}
|
||||
|
||||
static bool raw_sendok(Backend *be)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void raw_unthrottle(Backend *be, size_t backlog)
|
||||
{
|
||||
Raw *raw = container_of(be, Raw, backend);
|
||||
sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG);
|
||||
}
|
||||
|
||||
static bool raw_ldisc(Backend *be, int option)
|
||||
{
|
||||
if (option == LD_EDIT || option == LD_ECHO)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void raw_provide_ldisc(Backend *be, Ldisc *ldisc)
|
||||
{
|
||||
/* This is a stub. */
|
||||
}
|
||||
|
||||
static int raw_exitcode(Backend *be)
|
||||
{
|
||||
Raw *raw = container_of(be, Raw, backend);
|
||||
if (raw->s != NULL)
|
||||
return -1; /* still connected */
|
||||
else if (raw->closed_on_socket_error)
|
||||
return INT_MAX; /* a socket error counts as an unclean exit */
|
||||
else
|
||||
/* Exit codes are a meaningless concept in the Raw protocol */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cfg_info for Raw does nothing at all.
|
||||
*/
|
||||
static int raw_cfg_info(Backend *be)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const BackendVtable raw_backend = {
|
||||
.init = raw_init,
|
||||
.free = raw_free,
|
||||
.reconfig = raw_reconfig,
|
||||
.send = raw_send,
|
||||
.sendbuffer = raw_sendbuffer,
|
||||
.size = raw_size,
|
||||
.special = raw_special,
|
||||
.get_specials = raw_get_specials,
|
||||
.connected = raw_connected,
|
||||
.exitcode = raw_exitcode,
|
||||
.sendok = raw_sendok,
|
||||
.ldisc_option_state = raw_ldisc,
|
||||
.provide_ldisc = raw_provide_ldisc,
|
||||
.unthrottle = raw_unthrottle,
|
||||
.cfg_info = raw_cfg_info,
|
||||
.id = "raw",
|
||||
.displayname = "Raw",
|
||||
.protocol = PROT_RAW,
|
||||
.default_port = 0,
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Developer Studio generated include file.
|
||||
// Used by win_res.rc
|
||||
//
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +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, PlugLogType 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 = {
|
||||
.log = rlogin_log,
|
||||
.closing = rlogin_closing,
|
||||
.receive = rlogin_receive,
|
||||
.sent = 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 char *rlogin_init(const BackendVtable *vt, 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 = vt;
|
||||
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 dupstr(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 dupstr(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 BackendVtable rlogin_backend = {
|
||||
.init = rlogin_init,
|
||||
.free = rlogin_free,
|
||||
.reconfig = rlogin_reconfig,
|
||||
.send = rlogin_send,
|
||||
.sendbuffer = rlogin_sendbuffer,
|
||||
.size = rlogin_size,
|
||||
.special = rlogin_special,
|
||||
.get_specials = rlogin_get_specials,
|
||||
.connected = rlogin_connected,
|
||||
.exitcode = rlogin_exitcode,
|
||||
.sendok = rlogin_sendok,
|
||||
.ldisc_option_state = rlogin_ldisc,
|
||||
.provide_ldisc = rlogin_provide_ldisc,
|
||||
.unthrottle = rlogin_unthrottle,
|
||||
.cfg_info = rlogin_cfg_info,
|
||||
.id = "rlogin",
|
||||
.displayname = "Rlogin",
|
||||
.protocol = PROT_RLOGIN,
|
||||
.default_port = 513,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,787 @@
|
||||
/*
|
||||
* Implement the "session" channel type for the SSH server.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "sshchan.h"
|
||||
#include "sshserver.h"
|
||||
#include "sftp.h"
|
||||
|
||||
struct agentfwd {
|
||||
ConnectionLayer *cl;
|
||||
Socket *socket;
|
||||
Plug plug;
|
||||
};
|
||||
|
||||
typedef struct sesschan {
|
||||
SshChannel *c;
|
||||
|
||||
LogContext *parent_logctx, *child_logctx;
|
||||
Conf *conf;
|
||||
const SftpServerVtable *sftpserver_vt;
|
||||
|
||||
LogPolicy logpolicy;
|
||||
Seat seat;
|
||||
|
||||
bool want_pty;
|
||||
struct ssh_ttymodes ttymodes;
|
||||
int wc, hc, wp, hp;
|
||||
strbuf *termtype;
|
||||
|
||||
bool ignoring_input;
|
||||
bool seen_eof, seen_exit;
|
||||
|
||||
Plug xfwd_plug;
|
||||
int n_x11_sockets;
|
||||
Socket *x11_sockets[MAX_X11_SOCKETS];
|
||||
|
||||
agentfwd *agent;
|
||||
|
||||
Backend *backend;
|
||||
|
||||
bufchain subsys_input;
|
||||
SftpServer *sftpsrv;
|
||||
ScpServer *scpsrv;
|
||||
const SshServerConfig *ssc;
|
||||
|
||||
Channel chan;
|
||||
} sesschan;
|
||||
|
||||
static void sesschan_free(Channel *chan);
|
||||
static size_t sesschan_send(
|
||||
Channel *chan, bool is_stderr, const void *, size_t);
|
||||
static void sesschan_send_eof(Channel *chan);
|
||||
static char *sesschan_log_close_msg(Channel *chan);
|
||||
static bool sesschan_want_close(Channel *, bool, bool);
|
||||
static void sesschan_set_input_wanted(Channel *chan, bool wanted);
|
||||
static bool sesschan_run_shell(Channel *chan);
|
||||
static bool sesschan_run_command(Channel *chan, ptrlen command);
|
||||
static bool sesschan_run_subsystem(Channel *chan, ptrlen subsys);
|
||||
static bool sesschan_enable_x11_forwarding(
|
||||
Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata,
|
||||
unsigned screen_number);
|
||||
static bool sesschan_enable_agent_forwarding(Channel *chan);
|
||||
static bool sesschan_allocate_pty(
|
||||
Channel *chan, ptrlen termtype, unsigned width, unsigned height,
|
||||
unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes);
|
||||
static bool sesschan_set_env(Channel *chan, ptrlen var, ptrlen value);
|
||||
static bool sesschan_send_break(Channel *chan, unsigned length);
|
||||
static bool sesschan_send_signal(Channel *chan, ptrlen signame);
|
||||
static bool sesschan_change_window_size(
|
||||
Channel *chan, unsigned width, unsigned height,
|
||||
unsigned pixwidth, unsigned pixheight);
|
||||
|
||||
static const ChannelVtable sesschan_channelvt = {
|
||||
.free = sesschan_free,
|
||||
.open_confirmation = chan_remotely_opened_confirmation,
|
||||
.open_failed = chan_remotely_opened_failure,
|
||||
.send = sesschan_send,
|
||||
.send_eof = sesschan_send_eof,
|
||||
.set_input_wanted = sesschan_set_input_wanted,
|
||||
.log_close_msg = sesschan_log_close_msg,
|
||||
.want_close = sesschan_want_close,
|
||||
.rcvd_exit_status = chan_no_exit_status,
|
||||
.rcvd_exit_signal = chan_no_exit_signal,
|
||||
.rcvd_exit_signal_numeric = chan_no_exit_signal_numeric,
|
||||
.run_shell = sesschan_run_shell,
|
||||
.run_command = sesschan_run_command,
|
||||
.run_subsystem = sesschan_run_subsystem,
|
||||
.enable_x11_forwarding = sesschan_enable_x11_forwarding,
|
||||
.enable_agent_forwarding = sesschan_enable_agent_forwarding,
|
||||
.allocate_pty = sesschan_allocate_pty,
|
||||
.set_env = sesschan_set_env,
|
||||
.send_break = sesschan_send_break,
|
||||
.send_signal = sesschan_send_signal,
|
||||
.change_window_size = sesschan_change_window_size,
|
||||
.request_response = chan_no_request_response,
|
||||
};
|
||||
|
||||
static size_t sftp_chan_send(
|
||||
Channel *chan, bool is_stderr, const void *, size_t);
|
||||
static void sftp_chan_send_eof(Channel *chan);
|
||||
static char *sftp_log_close_msg(Channel *chan);
|
||||
|
||||
static const ChannelVtable sftp_channelvt = {
|
||||
.free = sesschan_free,
|
||||
.open_confirmation = chan_remotely_opened_confirmation,
|
||||
.open_failed = chan_remotely_opened_failure,
|
||||
.send = sftp_chan_send,
|
||||
.send_eof = sftp_chan_send_eof,
|
||||
.set_input_wanted = sesschan_set_input_wanted,
|
||||
.log_close_msg = sftp_log_close_msg,
|
||||
.want_close = chan_default_want_close,
|
||||
.rcvd_exit_status = chan_no_exit_status,
|
||||
.rcvd_exit_signal = chan_no_exit_signal,
|
||||
.rcvd_exit_signal_numeric = chan_no_exit_signal_numeric,
|
||||
.run_shell = chan_no_run_shell,
|
||||
.run_command = chan_no_run_command,
|
||||
.run_subsystem = chan_no_run_subsystem,
|
||||
.enable_x11_forwarding = chan_no_enable_x11_forwarding,
|
||||
.enable_agent_forwarding = chan_no_enable_agent_forwarding,
|
||||
.allocate_pty = chan_no_allocate_pty,
|
||||
.set_env = chan_no_set_env,
|
||||
.send_break = chan_no_send_break,
|
||||
.send_signal = chan_no_send_signal,
|
||||
.change_window_size = chan_no_change_window_size,
|
||||
.request_response = chan_no_request_response,
|
||||
};
|
||||
|
||||
static size_t scp_chan_send(
|
||||
Channel *chan, bool is_stderr, const void *, size_t);
|
||||
static void scp_chan_send_eof(Channel *chan);
|
||||
static void scp_set_input_wanted(Channel *chan, bool wanted);
|
||||
static char *scp_log_close_msg(Channel *chan);
|
||||
|
||||
static const ChannelVtable scp_channelvt = {
|
||||
.free = sesschan_free,
|
||||
.open_confirmation = chan_remotely_opened_confirmation,
|
||||
.open_failed = chan_remotely_opened_failure,
|
||||
.send = scp_chan_send,
|
||||
.send_eof = scp_chan_send_eof,
|
||||
.set_input_wanted = scp_set_input_wanted,
|
||||
.log_close_msg = scp_log_close_msg,
|
||||
.want_close = chan_default_want_close,
|
||||
.rcvd_exit_status = chan_no_exit_status,
|
||||
.rcvd_exit_signal = chan_no_exit_signal,
|
||||
.rcvd_exit_signal_numeric = chan_no_exit_signal_numeric,
|
||||
.run_shell = chan_no_run_shell,
|
||||
.run_command = chan_no_run_command,
|
||||
.run_subsystem = chan_no_run_subsystem,
|
||||
.enable_x11_forwarding = chan_no_enable_x11_forwarding,
|
||||
.enable_agent_forwarding = chan_no_enable_agent_forwarding,
|
||||
.allocate_pty = chan_no_allocate_pty,
|
||||
.set_env = chan_no_set_env,
|
||||
.send_break = chan_no_send_break,
|
||||
.send_signal = chan_no_send_signal,
|
||||
.change_window_size = chan_no_change_window_size,
|
||||
.request_response = chan_no_request_response,
|
||||
};
|
||||
|
||||
static void sesschan_eventlog(LogPolicy *lp, const char *event) {}
|
||||
static void sesschan_logging_error(LogPolicy *lp, const char *event) {}
|
||||
static int sesschan_askappend(
|
||||
LogPolicy *lp, Filename *filename,
|
||||
void (*callback)(void *ctx, int result), void *ctx) { return 2; }
|
||||
|
||||
static const LogPolicyVtable sesschan_logpolicy_vt = {
|
||||
.eventlog = sesschan_eventlog,
|
||||
.askappend = sesschan_askappend,
|
||||
.logging_error = sesschan_logging_error,
|
||||
.verbose = null_lp_verbose_no,
|
||||
};
|
||||
|
||||
static size_t sesschan_seat_output(
|
||||
Seat *, bool is_stderr, const void *, size_t);
|
||||
static bool sesschan_seat_eof(Seat *);
|
||||
static void sesschan_notify_remote_exit(Seat *seat);
|
||||
static void sesschan_connection_fatal(Seat *seat, const char *message);
|
||||
static bool sesschan_get_window_pixel_size(Seat *seat, int *w, int *h);
|
||||
|
||||
static const SeatVtable sesschan_seat_vt = {
|
||||
.output = sesschan_seat_output,
|
||||
.eof = sesschan_seat_eof,
|
||||
.get_userpass_input = nullseat_get_userpass_input,
|
||||
.notify_remote_exit = sesschan_notify_remote_exit,
|
||||
.connection_fatal = sesschan_connection_fatal,
|
||||
.update_specials_menu = nullseat_update_specials_menu,
|
||||
.get_ttymode = nullseat_get_ttymode,
|
||||
.set_busy_status = nullseat_set_busy_status,
|
||||
.verify_ssh_host_key = nullseat_verify_ssh_host_key,
|
||||
.confirm_weak_crypto_primitive = nullseat_confirm_weak_crypto_primitive,
|
||||
.confirm_weak_cached_hostkey = nullseat_confirm_weak_cached_hostkey,
|
||||
.is_utf8 = nullseat_is_never_utf8,
|
||||
.echoedit_update = nullseat_echoedit_update,
|
||||
.get_x_display = nullseat_get_x_display,
|
||||
.get_windowid = nullseat_get_windowid,
|
||||
.get_window_pixel_size = sesschan_get_window_pixel_size,
|
||||
.stripctrl_new = nullseat_stripctrl_new,
|
||||
.set_trust_status = nullseat_set_trust_status,
|
||||
.verbose = nullseat_verbose_no,
|
||||
.interactive = nullseat_interactive_no,
|
||||
.get_cursor_position = nullseat_get_cursor_position,
|
||||
};
|
||||
|
||||
Channel *sesschan_new(SshChannel *c, LogContext *logctx,
|
||||
const SftpServerVtable *sftpserver_vt,
|
||||
const SshServerConfig *ssc)
|
||||
{
|
||||
sesschan *sess = snew(sesschan);
|
||||
memset(sess, 0, sizeof(sesschan));
|
||||
|
||||
sess->c = c;
|
||||
sess->chan.vt = &sesschan_channelvt;
|
||||
sess->chan.initial_fixed_window_size = 0;
|
||||
sess->parent_logctx = logctx;
|
||||
sess->ssc = ssc;
|
||||
|
||||
/* Start with a completely default Conf */
|
||||
sess->conf = conf_new();
|
||||
load_open_settings(NULL, sess->conf);
|
||||
|
||||
/* Set close-on-exit = true to suppress uxpty.c's "[pterm: process
|
||||
* terminated with status x]" message */
|
||||
conf_set_int(sess->conf, CONF_close_on_exit, FORCE_ON);
|
||||
|
||||
sess->seat.vt = &sesschan_seat_vt;
|
||||
sess->logpolicy.vt = &sesschan_logpolicy_vt;
|
||||
sess->child_logctx = log_init(&sess->logpolicy, sess->conf);
|
||||
|
||||
sess->sftpserver_vt = sftpserver_vt;
|
||||
|
||||
bufchain_init(&sess->subsys_input);
|
||||
|
||||
return &sess->chan;
|
||||
}
|
||||
|
||||
static void sesschan_free(Channel *chan)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
int i;
|
||||
|
||||
delete_callbacks_for_context(sess);
|
||||
conf_free(sess->conf);
|
||||
if (sess->backend)
|
||||
backend_free(sess->backend);
|
||||
bufchain_clear(&sess->subsys_input);
|
||||
if (sess->sftpsrv)
|
||||
sftpsrv_free(sess->sftpsrv);
|
||||
for (i = 0; i < sess->n_x11_sockets; i++)
|
||||
sk_close(sess->x11_sockets[i]);
|
||||
if (sess->agent)
|
||||
agentfwd_free(sess->agent);
|
||||
|
||||
sfree(sess);
|
||||
}
|
||||
|
||||
static size_t sesschan_send(Channel *chan, bool is_stderr,
|
||||
const void *data, size_t length)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
|
||||
if (!sess->backend || sess->ignoring_input)
|
||||
return 0;
|
||||
|
||||
return backend_send(sess->backend, data, length);
|
||||
}
|
||||
|
||||
static void sesschan_send_eof(Channel *chan)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
if (sess->backend)
|
||||
backend_special(sess->backend, SS_EOF, 0);
|
||||
}
|
||||
|
||||
static char *sesschan_log_close_msg(Channel *chan)
|
||||
{
|
||||
return dupstr("Session channel closed");
|
||||
}
|
||||
|
||||
static void sesschan_set_input_wanted(Channel *chan, bool wanted)
|
||||
{
|
||||
/* I don't think we need to do anything here */
|
||||
}
|
||||
|
||||
static void sesschan_start_backend(sesschan *sess, const char *cmd)
|
||||
{
|
||||
/*
|
||||
* List of environment variables that we should not pass through
|
||||
* from the login session Uppity was run in (which, it being a
|
||||
* test server, there will usually be one of). These variables
|
||||
* will be set as part of X or agent forwarding, and shouldn't be
|
||||
* confusingly set in the absence of that.
|
||||
*
|
||||
* (DISPLAY must also be cleared, but uxpty.c will do that anyway
|
||||
* when our get_x_display method returns NULL.)
|
||||
*/
|
||||
static const char *const env_to_unset[] = {
|
||||
"XAUTHORITY", "SSH_AUTH_SOCK", "SSH_AGENT_PID",
|
||||
NULL /* terminator */
|
||||
};
|
||||
|
||||
sess->backend = pty_backend_create(
|
||||
&sess->seat, sess->child_logctx, sess->conf, NULL, cmd,
|
||||
sess->ttymodes, !sess->want_pty, sess->ssc->session_starting_dir,
|
||||
env_to_unset);
|
||||
backend_size(sess->backend, sess->wc, sess->hc);
|
||||
}
|
||||
|
||||
bool sesschan_run_shell(Channel *chan)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
|
||||
if (sess->backend)
|
||||
return false;
|
||||
|
||||
sesschan_start_backend(sess, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sesschan_run_command(Channel *chan, ptrlen command)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
|
||||
if (sess->backend)
|
||||
return false;
|
||||
|
||||
/* FIXME: make this possible to configure off */
|
||||
if ((sess->scpsrv = scp_recognise_exec(sess->c, sess->sftpserver_vt,
|
||||
command)) != NULL) {
|
||||
sess->chan.vt = &scp_channelvt;
|
||||
logevent(sess->parent_logctx, "Starting built-in SCP server");
|
||||
return true;
|
||||
}
|
||||
|
||||
char *command_str = mkstr(command);
|
||||
sesschan_start_backend(sess, command_str);
|
||||
sfree(command_str);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sesschan_run_subsystem(Channel *chan, ptrlen subsys)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
|
||||
if (ptrlen_eq_string(subsys, "sftp") && sess->sftpserver_vt) {
|
||||
sess->sftpsrv = sftpsrv_new(sess->sftpserver_vt);
|
||||
sess->chan.vt = &sftp_channelvt;
|
||||
logevent(sess->parent_logctx, "Starting built-in SFTP subsystem");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void fwd_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
|
||||
const char *error_msg, int error_code)
|
||||
{ /* don't expect any weirdnesses from a listening socket */ }
|
||||
static void fwd_closing(Plug *plug, const char *error_msg, int error_code,
|
||||
bool calling_back)
|
||||
{ /* not here, either */ }
|
||||
|
||||
static int xfwd_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
|
||||
{
|
||||
sesschan *sess = container_of(p, sesschan, xfwd_plug);
|
||||
Plug *plug;
|
||||
Channel *chan;
|
||||
Socket *s;
|
||||
SocketPeerInfo *pi;
|
||||
const char *err;
|
||||
|
||||
chan = portfwd_raw_new(sess->c->cl, &plug, false);
|
||||
s = constructor(ctx, plug);
|
||||
if ((err = sk_socket_error(s)) != NULL) {
|
||||
portfwd_raw_free(chan);
|
||||
return 1;
|
||||
}
|
||||
pi = sk_peer_info(s);
|
||||
portfwd_raw_setup(chan, s, ssh_serverside_x11_open(sess->c->cl, chan, pi));
|
||||
sk_free_peer_info(pi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const PlugVtable xfwd_plugvt = {
|
||||
.log = fwd_log,
|
||||
.closing = fwd_closing,
|
||||
.accepting = xfwd_accepting,
|
||||
};
|
||||
|
||||
bool sesschan_enable_x11_forwarding(
|
||||
Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata_hex,
|
||||
unsigned screen_number)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
strbuf *authdata_bin;
|
||||
size_t i;
|
||||
char screensuffix[32];
|
||||
|
||||
if (oneshot)
|
||||
return false; /* not supported */
|
||||
|
||||
snprintf(screensuffix, sizeof(screensuffix), ".%u", screen_number);
|
||||
|
||||
/*
|
||||
* Decode the authorisation data from ASCII hex into binary.
|
||||
*/
|
||||
if (authdata_hex.len % 2)
|
||||
return false; /* expected an even number of digits */
|
||||
authdata_bin = strbuf_new_nm();
|
||||
for (i = 0; i < authdata_hex.len; i += 2) {
|
||||
const unsigned char *hex = authdata_hex.ptr;
|
||||
char hexbuf[3];
|
||||
|
||||
if (!isxdigit(hex[i]) || !isxdigit(hex[i+1])) {
|
||||
strbuf_free(authdata_bin);
|
||||
return false; /* not hex */
|
||||
}
|
||||
|
||||
hexbuf[0] = hex[i];
|
||||
hexbuf[1] = hex[i+1];
|
||||
hexbuf[2] = '\0';
|
||||
put_byte(authdata_bin, strtoul(hexbuf, NULL, 16));
|
||||
}
|
||||
|
||||
sess->xfwd_plug.vt = &xfwd_plugvt;
|
||||
|
||||
sess->n_x11_sockets = platform_make_x11_server(
|
||||
&sess->xfwd_plug, appname, 10, screensuffix,
|
||||
authproto, ptrlen_from_strbuf(authdata_bin),
|
||||
sess->x11_sockets, sess->conf);
|
||||
|
||||
strbuf_free(authdata_bin);
|
||||
return sess->n_x11_sockets != 0;
|
||||
}
|
||||
|
||||
static int agentfwd_accepting(
|
||||
Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
|
||||
{
|
||||
agentfwd *agent = container_of(p, agentfwd, plug);
|
||||
Plug *plug;
|
||||
Channel *chan;
|
||||
Socket *s;
|
||||
const char *err;
|
||||
|
||||
chan = portfwd_raw_new(agent->cl, &plug, false);
|
||||
s = constructor(ctx, plug);
|
||||
if ((err = sk_socket_error(s)) != NULL) {
|
||||
portfwd_raw_free(chan);
|
||||
return 1;
|
||||
}
|
||||
portfwd_raw_setup(chan, s, ssh_serverside_agent_open(agent->cl, chan));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const PlugVtable agentfwd_plugvt = {
|
||||
.log = fwd_log,
|
||||
.closing = fwd_closing,
|
||||
.accepting = agentfwd_accepting,
|
||||
};
|
||||
|
||||
agentfwd *agentfwd_new(ConnectionLayer *cl, char **socketname_out)
|
||||
{
|
||||
agentfwd *agent = snew(agentfwd);
|
||||
agent->cl = cl;
|
||||
agent->plug.vt = &agentfwd_plugvt;
|
||||
|
||||
char *dir_prefix = dupprintf("/tmp/%s-agentfwd", appname);
|
||||
char *error = NULL, *socketname = NULL;
|
||||
agent->socket = platform_make_agent_socket(
|
||||
&agent->plug, dir_prefix, &error, &socketname);
|
||||
sfree(dir_prefix);
|
||||
sfree(error);
|
||||
|
||||
if (!agent->socket) {
|
||||
sfree(agent);
|
||||
sfree(socketname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*socketname_out = socketname;
|
||||
return agent;
|
||||
}
|
||||
|
||||
void agentfwd_free(agentfwd *agent)
|
||||
{
|
||||
if (agent->socket)
|
||||
sk_close(agent->socket);
|
||||
sfree(agent);
|
||||
}
|
||||
|
||||
bool sesschan_enable_agent_forwarding(Channel *chan)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
char *socketname;
|
||||
|
||||
assert(!sess->agent);
|
||||
|
||||
sess->agent = agentfwd_new(sess->c->cl, &socketname);
|
||||
|
||||
if (!sess->agent)
|
||||
return false;
|
||||
|
||||
conf_set_str_str(sess->conf, CONF_environmt, "SSH_AUTH_SOCK", socketname);
|
||||
sfree(socketname);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sesschan_allocate_pty(
|
||||
Channel *chan, ptrlen termtype, unsigned width, unsigned height,
|
||||
unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
char *s;
|
||||
|
||||
if (sess->want_pty)
|
||||
return false;
|
||||
|
||||
s = mkstr(termtype);
|
||||
conf_set_str(sess->conf, CONF_termtype, s);
|
||||
sfree(s);
|
||||
|
||||
sess->want_pty = true;
|
||||
sess->ttymodes = modes;
|
||||
sess->wc = width;
|
||||
sess->hc = height;
|
||||
sess->wp = pixwidth;
|
||||
sess->hp = pixheight;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sesschan_set_env(Channel *chan, ptrlen var, ptrlen value)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
|
||||
char *svar = mkstr(var), *svalue = mkstr(value);
|
||||
conf_set_str_str(sess->conf, CONF_environmt, svar, svalue);
|
||||
sfree(svar);
|
||||
sfree(svalue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sesschan_send_break(Channel *chan, unsigned length)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
|
||||
if (sess->backend) {
|
||||
/* We ignore the break length. We could pass it through as the
|
||||
* 'arg' parameter, and have uxpty.c collect it and pass it on
|
||||
* to tcsendbreak, but since tcsendbreak in turn assigns
|
||||
* implementation-defined semantics to _its_ duration
|
||||
* parameter, this all just sounds too difficult. */
|
||||
backend_special(sess->backend, SS_BRK, 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sesschan_send_signal(Channel *chan, ptrlen signame)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
|
||||
/* Start with a code that definitely isn't a signal (or indeed a
|
||||
* special command at all), to indicate 'nothing matched'. */
|
||||
SessionSpecialCode code = SS_EXITMENU;
|
||||
|
||||
#define SIGNAL_SUB(name) \
|
||||
if (ptrlen_eq_string(signame, #name)) code = SS_SIG ## name;
|
||||
#define SIGNAL_MAIN(name, text) SIGNAL_SUB(name)
|
||||
#include "sshsignals.h"
|
||||
#undef SIGNAL_MAIN
|
||||
#undef SIGNAL_SUB
|
||||
|
||||
if (code == SS_EXITMENU)
|
||||
return false;
|
||||
|
||||
backend_special(sess->backend, code, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sesschan_change_window_size(
|
||||
Channel *chan, unsigned width, unsigned height,
|
||||
unsigned pixwidth, unsigned pixheight)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
|
||||
if (!sess->want_pty)
|
||||
return false;
|
||||
|
||||
sess->wc = width;
|
||||
sess->hc = height;
|
||||
sess->wp = pixwidth;
|
||||
sess->hp = pixheight;
|
||||
|
||||
if (sess->backend)
|
||||
backend_size(sess->backend, sess->wc, sess->hc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t sesschan_seat_output(
|
||||
Seat *seat, bool is_stderr, const void *data, size_t len)
|
||||
{
|
||||
sesschan *sess = container_of(seat, sesschan, seat);
|
||||
return sshfwd_write_ext(sess->c, is_stderr, data, len);
|
||||
}
|
||||
|
||||
static void sesschan_check_close_callback(void *vctx)
|
||||
{
|
||||
sesschan *sess = (sesschan *)vctx;
|
||||
|
||||
/*
|
||||
* Once we've seen incoming EOF from the backend (aka EIO from the
|
||||
* pty master) and also passed on the process's exit status, we
|
||||
* should proactively initiate closure of the session channel.
|
||||
*/
|
||||
if (sess->seen_eof && sess->seen_exit)
|
||||
sshfwd_initiate_close(sess->c, NULL);
|
||||
}
|
||||
|
||||
static bool sesschan_want_close(Channel *chan, bool seen_eof, bool rcvd_eof)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
|
||||
/*
|
||||
* Similarly to above, we don't want to initiate channel closure
|
||||
* until we've sent the process's exit status, _even_ if EOF of
|
||||
* the actual data stream has happened in both directions.
|
||||
*/
|
||||
return (sess->seen_eof && sess->seen_exit);
|
||||
}
|
||||
|
||||
static bool sesschan_seat_eof(Seat *seat)
|
||||
{
|
||||
sesschan *sess = container_of(seat, sesschan, seat);
|
||||
|
||||
sshfwd_write_eof(sess->c);
|
||||
sess->seen_eof = true;
|
||||
|
||||
queue_toplevel_callback(sesschan_check_close_callback, sess);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sesschan_notify_remote_exit(Seat *seat)
|
||||
{
|
||||
sesschan *sess = container_of(seat, sesschan, seat);
|
||||
|
||||
if (!sess->backend)
|
||||
return;
|
||||
|
||||
bool got_signal = false;
|
||||
if (!sess->ssc->exit_signal_numeric) {
|
||||
char *sigmsg;
|
||||
ptrlen signame = pty_backend_exit_signame(sess->backend, &sigmsg);
|
||||
|
||||
if (signame.len) {
|
||||
if (!sigmsg)
|
||||
sigmsg = dupstr("");
|
||||
|
||||
sshfwd_send_exit_signal(
|
||||
sess->c, signame, false, ptrlen_from_asciz(sigmsg));
|
||||
|
||||
got_signal = true;
|
||||
}
|
||||
|
||||
sfree(sigmsg);
|
||||
} else {
|
||||
int signum = pty_backend_exit_signum(sess->backend);
|
||||
|
||||
if (signum >= 0) {
|
||||
sshfwd_send_exit_signal_numeric(sess->c, signum, false,
|
||||
PTRLEN_LITERAL(""));
|
||||
got_signal = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!got_signal)
|
||||
sshfwd_send_exit_status(sess->c, backend_exitcode(sess->backend));
|
||||
|
||||
sess->seen_exit = true;
|
||||
queue_toplevel_callback(sesschan_check_close_callback, sess);
|
||||
}
|
||||
|
||||
static void sesschan_connection_fatal(Seat *seat, const char *message)
|
||||
{
|
||||
sesschan *sess = container_of(seat, sesschan, seat);
|
||||
|
||||
/* Closest translation I can think of */
|
||||
sshfwd_send_exit_signal(
|
||||
sess->c, PTRLEN_LITERAL("HUP"), false, ptrlen_from_asciz(message));
|
||||
|
||||
sess->ignoring_input = true;
|
||||
}
|
||||
|
||||
static bool sesschan_get_window_pixel_size(Seat *seat, int *width, int *height)
|
||||
{
|
||||
sesschan *sess = container_of(seat, sesschan, seat);
|
||||
|
||||
*width = sess->wp;
|
||||
*height = sess->hp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Built-in SFTP subsystem.
|
||||
*/
|
||||
|
||||
static size_t sftp_chan_send(Channel *chan, bool is_stderr,
|
||||
const void *data, size_t length)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
|
||||
bufchain_add(&sess->subsys_input, data, length);
|
||||
|
||||
while (bufchain_size(&sess->subsys_input) >= 4) {
|
||||
char lenbuf[4];
|
||||
unsigned pktlen;
|
||||
struct sftp_packet *pkt, *reply;
|
||||
|
||||
bufchain_fetch(&sess->subsys_input, lenbuf, 4);
|
||||
pktlen = GET_32BIT_MSB_FIRST(lenbuf);
|
||||
|
||||
if (bufchain_size(&sess->subsys_input) - 4 < pktlen)
|
||||
break; /* wait for more data */
|
||||
|
||||
bufchain_consume(&sess->subsys_input, 4);
|
||||
pkt = sftp_recv_prepare(pktlen);
|
||||
bufchain_fetch_consume(&sess->subsys_input, pkt->data, pktlen);
|
||||
sftp_recv_finish(pkt);
|
||||
reply = sftp_handle_request(sess->sftpsrv, pkt);
|
||||
sftp_pkt_free(pkt);
|
||||
|
||||
sftp_send_prepare(reply);
|
||||
sshfwd_write(sess->c, reply->data, reply->length);
|
||||
sftp_pkt_free(reply);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sftp_chan_send_eof(Channel *chan)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
sshfwd_write_eof(sess->c);
|
||||
}
|
||||
|
||||
static char *sftp_log_close_msg(Channel *chan)
|
||||
{
|
||||
return dupstr("Session channel (SFTP) closed");
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Built-in SCP subsystem.
|
||||
*/
|
||||
|
||||
static size_t scp_chan_send(Channel *chan, bool is_stderr,
|
||||
const void *data, size_t length)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
return scp_send(sess->scpsrv, data, length);
|
||||
}
|
||||
|
||||
static void scp_chan_send_eof(Channel *chan)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
scp_eof(sess->scpsrv);
|
||||
}
|
||||
|
||||
static char *scp_log_close_msg(Channel *chan)
|
||||
{
|
||||
return dupstr("Session channel (SCP) closed");
|
||||
}
|
||||
|
||||
static void scp_set_input_wanted(Channel *chan, bool wanted)
|
||||
{
|
||||
sesschan *sess = container_of(chan, sesschan, chan);
|
||||
scp_throttle(sess->scpsrv, !wanted);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user